1342 lines
35 KiB
C
1342 lines
35 KiB
C
/*
|
|
* Copyright © 2019-2020 Stefan Agner <stefan@agner.ch>
|
|
* Copyright © 2021-2022 Pengutronix, Philipp Zabel <p.zabel@pengutronix.de>
|
|
* Copyright © 2022 Pengutronix, Rouven Czerwinski <r.czerwinski@pengutronix.de>
|
|
* based on backend-rdp:
|
|
* Copyright © 2013 Hardening <rdp.effort@gmail.com>
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person obtaining
|
|
* a copy of this software and associated documentation files (the
|
|
* "Software"), to deal in the Software without restriction, including
|
|
* without limitation the rights to use, copy, modify, merge, publish,
|
|
* distribute, sublicense, and/or sell copies of the Software, and to
|
|
* permit persons to whom the Software is furnished to do so, subject to
|
|
* the following conditions:
|
|
*
|
|
* The above copyright notice and this permission notice (including the
|
|
* next paragraph) shall be included in all copies or substantial
|
|
* portions of the Software.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
|
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
|
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
|
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
* SOFTWARE.
|
|
*/
|
|
|
|
#include "config.h"
|
|
|
|
#include <assert.h>
|
|
#include <stdint.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <errno.h>
|
|
#include <linux/input.h>
|
|
#include <netinet/in.h>
|
|
#include <pwd.h>
|
|
#include <sys/types.h>
|
|
#include <sys/socket.h>
|
|
#include <unistd.h>
|
|
#include <xkbcommon/xkbcommon-keysyms.h>
|
|
#include <xkbcommon/xkbcommon.h>
|
|
#define AML_UNSTABLE_API 1
|
|
#include <aml.h>
|
|
#include <neatvnc.h>
|
|
#include <drm_fourcc.h>
|
|
|
|
#include "shared/helpers.h"
|
|
#include "shared/xalloc.h"
|
|
#include "shared/timespec-util.h"
|
|
#include <libweston/libweston.h>
|
|
#include <libweston/backend-vnc.h>
|
|
#include <libweston/weston-log.h>
|
|
#include "pixel-formats.h"
|
|
#include "pixman-renderer.h"
|
|
#include "renderer-gl/gl-renderer.h"
|
|
#include "shared/weston-egl-ext.h"
|
|
|
|
#define DEFAULT_AXIS_STEP_DISTANCE 10
|
|
|
|
struct vnc_output;
|
|
|
|
struct vnc_backend {
|
|
struct weston_backend base;
|
|
struct weston_compositor *compositor;
|
|
struct weston_log_scope *debug;
|
|
struct vnc_output *output;
|
|
|
|
struct xkb_rule_names xkb_rule_name;
|
|
struct xkb_keymap *xkb_keymap;
|
|
|
|
struct aml *aml;
|
|
struct wl_event_source *aml_event;
|
|
struct nvnc *server;
|
|
int vnc_monitor_refresh_rate;
|
|
|
|
const struct pixel_format_info **formats;
|
|
unsigned int formats_count;
|
|
};
|
|
|
|
struct vnc_output {
|
|
struct weston_output base;
|
|
struct weston_plane cursor_plane;
|
|
struct weston_surface *cursor_surface;
|
|
struct vnc_backend *backend;
|
|
struct wl_event_source *finish_frame_timer;
|
|
struct nvnc_display *display;
|
|
|
|
struct nvnc_fb_pool *fb_pool;
|
|
|
|
struct wl_list peers;
|
|
|
|
bool resizeable;
|
|
};
|
|
|
|
struct vnc_peer {
|
|
struct vnc_backend *backend;
|
|
struct weston_seat *seat;
|
|
struct nvnc_client *client;
|
|
|
|
enum nvnc_button_mask last_button_mask;
|
|
struct wl_list link;
|
|
};
|
|
|
|
struct vnc_head {
|
|
struct weston_head base;
|
|
};
|
|
|
|
static void
|
|
vnc_output_destroy(struct weston_output *base);
|
|
|
|
static inline struct vnc_output *
|
|
to_vnc_output(struct weston_output *base)
|
|
{
|
|
if (base->destroy != vnc_output_destroy)
|
|
return NULL;
|
|
return container_of(base, struct vnc_output, base);
|
|
}
|
|
|
|
static void
|
|
vnc_head_destroy(struct weston_head *base);
|
|
|
|
static void
|
|
vnc_destroy(struct weston_backend *backend);
|
|
|
|
static inline struct vnc_head *
|
|
to_vnc_head(struct weston_head *base)
|
|
{
|
|
if (base->backend->destroy != vnc_destroy)
|
|
return NULL;
|
|
return container_of(base, struct vnc_head, base);
|
|
}
|
|
|
|
struct vnc_keysym_to_keycode {
|
|
const uint32_t keysym;
|
|
const uint32_t code;
|
|
const bool shift;
|
|
};
|
|
|
|
static const
|
|
struct vnc_keysym_to_keycode key_translation[] = {
|
|
{XKB_KEY_KP_Enter, 0x60, false },
|
|
{XKB_KEY_Return, 0x1c, false },
|
|
{XKB_KEY_space, 0x39, false },
|
|
{XKB_KEY_BackSpace, 0xe, false },
|
|
{XKB_KEY_Tab, 0xf, false },
|
|
{XKB_KEY_Escape, 0x1, false },
|
|
{XKB_KEY_Shift_L, 0x2a, false },
|
|
{XKB_KEY_Shift_R, 0x36, false },
|
|
{XKB_KEY_Control_L, 0x1d, false },
|
|
{XKB_KEY_Control_R, 0x9d, false },
|
|
{XKB_KEY_Alt_L, 0x38, false },
|
|
{XKB_KEY_Alt_R, 0x64, false },
|
|
{XKB_KEY_Meta_L, 0x38, false },
|
|
{XKB_KEY_Meta_R, 0x64, false },
|
|
{XKB_KEY_Super_L, 0x7d, false },
|
|
{XKB_KEY_Print, 0x63, false },
|
|
{XKB_KEY_Pause, 0x77, false },
|
|
{XKB_KEY_Caps_Lock, 0x3a, false },
|
|
{XKB_KEY_Scroll_Lock, 0x46, false },
|
|
{XKB_KEY_A, 0x1e, true },
|
|
{XKB_KEY_a, 0x1e, false },
|
|
{XKB_KEY_B, 0x30, true },
|
|
{XKB_KEY_b, 0x30, false },
|
|
{XKB_KEY_C, 0x2e, true },
|
|
{XKB_KEY_c, 0x2e, false },
|
|
{XKB_KEY_D, 0x20, true },
|
|
{XKB_KEY_d, 0x20, false },
|
|
{XKB_KEY_E, 0x12, true },
|
|
{XKB_KEY_e, 0x12, false },
|
|
{XKB_KEY_F, 0x21, true },
|
|
{XKB_KEY_f, 0x21, false },
|
|
{XKB_KEY_G, 0x22, true },
|
|
{XKB_KEY_g, 0x22, false },
|
|
{XKB_KEY_H, 0x23, true },
|
|
{XKB_KEY_h, 0x23, false },
|
|
{XKB_KEY_I, 0x17, true },
|
|
{XKB_KEY_i, 0x17, false },
|
|
{XKB_KEY_J, 0x24, true },
|
|
{XKB_KEY_j, 0x24, false },
|
|
{XKB_KEY_K, 0x25, true },
|
|
{XKB_KEY_k, 0x25, false },
|
|
{XKB_KEY_L, 0x26, true },
|
|
{XKB_KEY_l, 0x26, false },
|
|
{XKB_KEY_M, 0x32, true },
|
|
{XKB_KEY_m, 0x32, false },
|
|
{XKB_KEY_N, 0x31, true },
|
|
{XKB_KEY_n, 0x31, false },
|
|
{XKB_KEY_O, 0x18, true },
|
|
{XKB_KEY_o, 0x18, false },
|
|
{XKB_KEY_P, 0x19, true },
|
|
{XKB_KEY_p, 0x19, false },
|
|
{XKB_KEY_Q, 0x10, true },
|
|
{XKB_KEY_q, 0x10, false },
|
|
{XKB_KEY_R, 0x13, true },
|
|
{XKB_KEY_r, 0x13, false },
|
|
{XKB_KEY_S, 0x1f, true },
|
|
{XKB_KEY_s, 0x1f, false },
|
|
{XKB_KEY_T, 0x14, true },
|
|
{XKB_KEY_t, 0x14, false },
|
|
{XKB_KEY_U, 0x16, true },
|
|
{XKB_KEY_u, 0x16, false },
|
|
{XKB_KEY_V, 0x2f, true },
|
|
{XKB_KEY_v, 0x2f, false },
|
|
{XKB_KEY_W, 0x11, true },
|
|
{XKB_KEY_w, 0x11, false },
|
|
{XKB_KEY_X, 0x2d, true },
|
|
{XKB_KEY_x, 0x2d, false },
|
|
{XKB_KEY_Y, 0x15, true },
|
|
{XKB_KEY_y, 0x15, false },
|
|
{XKB_KEY_Z, 0x2c, true },
|
|
{XKB_KEY_z, 0x2c, false },
|
|
{XKB_KEY_grave, 0x29, false },
|
|
{XKB_KEY_asciitilde, 0x29, true },
|
|
{XKB_KEY_1, 0x02, false },
|
|
{XKB_KEY_exclam, 0x02, true },
|
|
{XKB_KEY_2, 0x03, false },
|
|
{XKB_KEY_at, 0x03, true },
|
|
{XKB_KEY_3, 0x04, false },
|
|
{XKB_KEY_numbersign, 0x04, true },
|
|
{XKB_KEY_4, 0x05, false },
|
|
{XKB_KEY_dollar, 0x05, true },
|
|
{XKB_KEY_5, 0x06, false },
|
|
{XKB_KEY_percent, 0x06, true },
|
|
{XKB_KEY_6, 0x07, false },
|
|
{XKB_KEY_asciicircum, 0x07, true },
|
|
{XKB_KEY_7, 0x08, false },
|
|
{XKB_KEY_ampersand, 0x08, true },
|
|
{XKB_KEY_8, 0x09, false },
|
|
{XKB_KEY_asterisk, 0x09, true },
|
|
{XKB_KEY_9, 0x0a, false },
|
|
{XKB_KEY_parenleft, 0x0a, true },
|
|
{XKB_KEY_0, 0x0b, false },
|
|
{XKB_KEY_parenright, 0x0b, true },
|
|
{XKB_KEY_minus, 0x0c, false, },
|
|
{XKB_KEY_underscore, 0x0c, true },
|
|
{XKB_KEY_equal, 0x0d, false },
|
|
{XKB_KEY_plus, 0x0d, true },
|
|
{XKB_KEY_bracketleft, 0x1a, false },
|
|
{XKB_KEY_braceleft, 0x1a, true },
|
|
{XKB_KEY_bracketright, 0x1b, false },
|
|
{XKB_KEY_braceright, 0x1b, true },
|
|
{XKB_KEY_semicolon, 0x27, false },
|
|
{XKB_KEY_colon, 0x27, true },
|
|
{XKB_KEY_apostrophe, 0x28, false },
|
|
{XKB_KEY_quotedbl, 0x28, true },
|
|
{XKB_KEY_backslash, 0x2b, false },
|
|
{XKB_KEY_bar, 0x2b, true },
|
|
{XKB_KEY_comma, 0x33, false },
|
|
{XKB_KEY_less, 0x33, true },
|
|
{XKB_KEY_period, 0x34, false },
|
|
{XKB_KEY_greater, 0x34, true },
|
|
{XKB_KEY_slash, 0x35, false },
|
|
{XKB_KEY_question, 0x35, true },
|
|
{XKB_KEY_F1, 0x3b, false },
|
|
{XKB_KEY_F2, 0x3c, false },
|
|
{XKB_KEY_F3, 0x3d, false },
|
|
{XKB_KEY_F4, 0x3e, false },
|
|
{XKB_KEY_F5, 0x3f, false },
|
|
{XKB_KEY_F6, 0x40, false },
|
|
{XKB_KEY_F7, 0x41, false },
|
|
{XKB_KEY_F8, 0x42, false },
|
|
{XKB_KEY_F9, 0x43, false },
|
|
{XKB_KEY_F10, 0x44, false },
|
|
{XKB_KEY_F11, 0x57, false },
|
|
{XKB_KEY_F12, 0x58, false },
|
|
{XKB_KEY_Home, 0x66, false },
|
|
{XKB_KEY_Up, 0x67, false },
|
|
{XKB_KEY_Prior, 0x68, false },
|
|
{XKB_KEY_Left, 0x69, false },
|
|
{XKB_KEY_Right, 0x6a, false },
|
|
{XKB_KEY_End, 0x6b, false },
|
|
{XKB_KEY_Down, 0x6c, false },
|
|
{XKB_KEY_Next, 0x6d, false },
|
|
{ },
|
|
};
|
|
|
|
static const uint32_t vnc_formats[] = {
|
|
DRM_FORMAT_XRGB8888,
|
|
DRM_FORMAT_ARGB8888,
|
|
};
|
|
|
|
static void
|
|
vnc_handle_key_event(struct nvnc_client *client, uint32_t keysym,
|
|
bool is_pressed)
|
|
{
|
|
struct vnc_peer *peer = nvnc_get_userdata(client);
|
|
uint32_t key = 0;
|
|
bool needs_shift = false;
|
|
enum weston_key_state_update state_update;
|
|
enum wl_keyboard_key_state state;
|
|
struct timespec time;
|
|
int i;
|
|
|
|
weston_compositor_get_time(&time);
|
|
|
|
if (is_pressed)
|
|
state = WL_KEYBOARD_KEY_STATE_PRESSED;
|
|
else
|
|
state = WL_KEYBOARD_KEY_STATE_RELEASED;
|
|
|
|
/* Generally ignore shift state as per RFC6143 Section 7.5.4 */
|
|
if (keysym == XKB_KEY_Shift_L || keysym == XKB_KEY_Shift_R)
|
|
return;
|
|
|
|
/* Allow selected modifiers */
|
|
if (keysym == XKB_KEY_Control_L || keysym == XKB_KEY_Control_R ||
|
|
keysym == XKB_KEY_Alt_L || keysym == XKB_KEY_Alt_R)
|
|
state_update = STATE_UPDATE_AUTOMATIC;
|
|
else
|
|
state_update = STATE_UPDATE_NONE;
|
|
|
|
for (i = 0; key_translation[i].keysym; i++) {
|
|
if (key_translation[i].keysym == keysym) {
|
|
key = key_translation[i].code;
|
|
needs_shift = key_translation[i].shift;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!key) {
|
|
weston_log("Key not found: keysym %08x, translated %08x\n",
|
|
keysym, key);
|
|
return;
|
|
}
|
|
|
|
/* emulate lshift press */
|
|
if (needs_shift)
|
|
notify_key(peer->seat, &time, KEY_LEFTSHIFT,
|
|
WL_KEYBOARD_KEY_STATE_PRESSED,
|
|
STATE_UPDATE_AUTOMATIC);
|
|
|
|
/* send detected key code */
|
|
notify_key(peer->seat, &time, key, state, state_update);
|
|
|
|
/* emulate lshift release */
|
|
if (needs_shift)
|
|
notify_key(peer->seat, &time, KEY_LEFTSHIFT,
|
|
WL_KEYBOARD_KEY_STATE_RELEASED,
|
|
STATE_UPDATE_AUTOMATIC);
|
|
}
|
|
|
|
static void
|
|
vnc_handle_key_code_event(struct nvnc_client *client, uint32_t key,
|
|
bool is_pressed)
|
|
{
|
|
struct vnc_peer *peer = nvnc_get_userdata(client);
|
|
enum wl_keyboard_key_state state;
|
|
struct timespec time;
|
|
|
|
weston_compositor_get_time(&time);
|
|
|
|
if (is_pressed)
|
|
state = WL_KEYBOARD_KEY_STATE_PRESSED;
|
|
else
|
|
state = WL_KEYBOARD_KEY_STATE_RELEASED;
|
|
|
|
notify_key(peer->seat, &time, key, state, STATE_UPDATE_AUTOMATIC);
|
|
}
|
|
|
|
static void
|
|
vnc_log_desktop_layout(struct vnc_backend *backend,
|
|
const struct nvnc_desktop_layout *layout)
|
|
{
|
|
uint8_t num_displays = nvnc_desktop_layout_get_display_count(layout);
|
|
char timestr[128];
|
|
|
|
if (!weston_log_scope_is_enabled(backend->debug))
|
|
return;
|
|
|
|
weston_log_scope_timestamp(backend->debug, timestr, sizeof timestr);
|
|
|
|
weston_log_scope_printf(backend->debug,
|
|
"%s desktop size event, %u displays:",
|
|
timestr, num_displays);
|
|
for (int i = 0; i < num_displays; i++) {
|
|
uint16_t x = nvnc_desktop_layout_get_display_x_pos(layout, i);
|
|
uint16_t y = nvnc_desktop_layout_get_display_y_pos(layout, i);
|
|
uint16_t width = nvnc_desktop_layout_get_display_width(layout, i);
|
|
uint16_t height = nvnc_desktop_layout_get_display_height(layout, i);
|
|
struct nvnc_display *display = nvnc_desktop_layout_get_display(layout, i);
|
|
|
|
weston_log_scope_printf(backend->debug, " %ux%u(%u,%u)%s",
|
|
width, height, x, y,
|
|
display ? "" : "*");
|
|
}
|
|
weston_log_scope_printf(backend->debug, "\n");
|
|
}
|
|
|
|
static bool
|
|
vnc_handle_desktop_layout_event(struct nvnc_client *client,
|
|
const struct nvnc_desktop_layout *layout)
|
|
{
|
|
struct vnc_peer *peer = nvnc_get_userdata(client);
|
|
struct vnc_output *output = peer->backend->output;
|
|
struct weston_mode new_mode;
|
|
uint16_t width = nvnc_desktop_layout_get_width(layout);
|
|
uint16_t height = nvnc_desktop_layout_get_height(layout);
|
|
|
|
vnc_log_desktop_layout(peer->backend, layout);
|
|
|
|
if (!output->resizeable)
|
|
return false;
|
|
|
|
new_mode.width = width;
|
|
new_mode.height = height;
|
|
new_mode.refresh = peer->backend->vnc_monitor_refresh_rate;
|
|
|
|
weston_output_mode_set_native(&output->base, &new_mode, 1);
|
|
|
|
return true;
|
|
}
|
|
|
|
static void
|
|
vnc_pointer_event(struct nvnc_client *client, uint16_t x, uint16_t y,
|
|
enum nvnc_button_mask button_mask)
|
|
{
|
|
struct vnc_peer *peer = nvnc_get_userdata(client);
|
|
struct vnc_output *output = peer->backend->output;
|
|
struct timespec time;
|
|
enum nvnc_button_mask changed_button_mask;
|
|
|
|
weston_compositor_get_time(&time);
|
|
|
|
if (x < output->base.width && y < output->base.height) {
|
|
struct weston_coord_global pos;
|
|
|
|
pos = weston_coord_global_from_output_point(x, y, &output->base);
|
|
notify_motion_absolute(peer->seat, &time, pos);
|
|
}
|
|
|
|
changed_button_mask = peer->last_button_mask ^ button_mask;
|
|
|
|
if (changed_button_mask & NVNC_BUTTON_LEFT)
|
|
notify_button(peer->seat, &time, BTN_LEFT,
|
|
(button_mask & NVNC_BUTTON_LEFT) ?
|
|
WL_POINTER_BUTTON_STATE_PRESSED :
|
|
WL_POINTER_BUTTON_STATE_RELEASED);
|
|
|
|
if (changed_button_mask & NVNC_BUTTON_MIDDLE)
|
|
notify_button(peer->seat, &time, BTN_MIDDLE,
|
|
(button_mask & NVNC_BUTTON_MIDDLE) ?
|
|
WL_POINTER_BUTTON_STATE_PRESSED :
|
|
WL_POINTER_BUTTON_STATE_RELEASED);
|
|
|
|
if (changed_button_mask & NVNC_BUTTON_RIGHT)
|
|
notify_button(peer->seat, &time, BTN_RIGHT,
|
|
(button_mask & NVNC_BUTTON_RIGHT) ?
|
|
WL_POINTER_BUTTON_STATE_PRESSED :
|
|
WL_POINTER_BUTTON_STATE_RELEASED);
|
|
|
|
if ((button_mask & NVNC_SCROLL_UP) ||
|
|
(button_mask & NVNC_SCROLL_DOWN)) {
|
|
struct weston_pointer_axis_event weston_event;
|
|
|
|
weston_event.axis = WL_POINTER_AXIS_VERTICAL_SCROLL;
|
|
|
|
/* DEFAULT_AXIS_STEP_DISTANCE is stolen from compositor-x11.c */
|
|
if (button_mask & NVNC_SCROLL_UP)
|
|
weston_event.value = -DEFAULT_AXIS_STEP_DISTANCE;
|
|
if (button_mask & NVNC_SCROLL_DOWN)
|
|
weston_event.value = DEFAULT_AXIS_STEP_DISTANCE;
|
|
weston_event.has_discrete = false;
|
|
|
|
notify_axis(peer->seat, &time, &weston_event);
|
|
}
|
|
|
|
peer->last_button_mask = button_mask;
|
|
|
|
notify_pointer_frame(peer->seat);
|
|
}
|
|
|
|
static bool
|
|
vnc_handle_auth(const char *username, const char *password, void *userdata)
|
|
{
|
|
struct passwd *pw = getpwnam(username);
|
|
|
|
if (!pw || pw->pw_uid != getuid()) {
|
|
weston_log("VNC: wrong user '%s'\n", username);
|
|
return false;
|
|
}
|
|
|
|
return weston_authenticate_user(username, password);
|
|
}
|
|
|
|
static void
|
|
vnc_client_cleanup(struct nvnc_client *client)
|
|
{
|
|
struct vnc_peer *peer = nvnc_get_userdata(client);
|
|
struct vnc_output *output = peer->backend->output;
|
|
|
|
wl_list_remove(&peer->link);
|
|
weston_seat_release_keyboard(peer->seat);
|
|
weston_seat_release_pointer(peer->seat);
|
|
weston_seat_release(peer->seat);
|
|
free(peer);
|
|
weston_log("VNC Client disconnected\n");
|
|
|
|
if (output && wl_list_empty(&output->peers))
|
|
weston_output_power_off(&output->base);
|
|
}
|
|
|
|
static struct weston_pointer *
|
|
vnc_output_get_pointer(struct vnc_output *output,
|
|
struct weston_paint_node **pointer_pnode)
|
|
{
|
|
struct weston_pointer *pointer = NULL;
|
|
struct weston_paint_node *pnode;
|
|
struct vnc_peer *peer;
|
|
|
|
wl_list_for_each(peer, &output->peers, link) {
|
|
pointer = weston_seat_get_pointer(peer->seat);
|
|
break;
|
|
}
|
|
|
|
if (!pointer)
|
|
return NULL;
|
|
|
|
wl_list_for_each(pnode, &output->base.paint_node_z_order_list, z_order_link) {
|
|
if (pnode->view == pointer->sprite) {
|
|
*pointer_pnode = pnode;
|
|
return pointer;
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static void
|
|
vnc_output_update_cursor(struct vnc_output *output)
|
|
{
|
|
struct vnc_backend *backend = output->backend;
|
|
struct weston_pointer *pointer;
|
|
struct weston_paint_node *pointer_pnode = NULL;
|
|
bool update_cursor;
|
|
pixman_region32_t damage;
|
|
struct weston_buffer *buffer;
|
|
struct weston_surface *cursor_surface;
|
|
struct nvnc_fb *fb;
|
|
uint8_t *src, *dst;
|
|
int i;
|
|
|
|
pointer = vnc_output_get_pointer(output, &pointer_pnode);
|
|
|
|
pixman_region32_init(&damage);
|
|
weston_output_flush_damage_for_plane(&output->base, &output->cursor_plane, &damage);
|
|
update_cursor = pixman_region32_not_empty(&damage);
|
|
pixman_region32_fini(&damage);
|
|
|
|
if (!update_cursor)
|
|
return;
|
|
|
|
cursor_surface = output->cursor_surface;
|
|
buffer = cursor_surface->buffer_ref.buffer;
|
|
|
|
fb = nvnc_fb_new(buffer->width, buffer->height, DRM_FORMAT_ARGB8888,
|
|
buffer->width);
|
|
assert(fb);
|
|
|
|
src = wl_shm_buffer_get_data(buffer->shm_buffer);
|
|
dst = nvnc_fb_get_addr(fb);
|
|
|
|
wl_shm_buffer_begin_access(buffer->shm_buffer);
|
|
for (i = 0; i < buffer->height; i++)
|
|
memcpy(dst + i * 4 * buffer->width, src + i * buffer->stride,
|
|
4 * buffer->width);
|
|
wl_shm_buffer_end_access(buffer->shm_buffer);
|
|
|
|
nvnc_set_cursor(backend->server, fb, buffer->width, buffer->height,
|
|
pointer->hotspot.c.x, pointer->hotspot.c.y, true);
|
|
nvnc_fb_unref(fb);
|
|
}
|
|
|
|
static void
|
|
vnc_output_assign_cursor_plane(struct vnc_output *output)
|
|
{
|
|
struct weston_pointer *pointer;
|
|
struct weston_paint_node *pointer_pnode = NULL;
|
|
struct weston_view *view;
|
|
struct weston_buffer *buffer;
|
|
uint32_t format;
|
|
|
|
pointer = vnc_output_get_pointer(output, &pointer_pnode);
|
|
if (!pointer)
|
|
return;
|
|
|
|
view = pointer->sprite;
|
|
if (!weston_view_has_valid_buffer(view))
|
|
return;
|
|
|
|
buffer = view->surface->buffer_ref.buffer;
|
|
if (buffer->type != WESTON_BUFFER_SHM)
|
|
return;
|
|
|
|
format = wl_shm_buffer_get_format(buffer->shm_buffer);
|
|
if (format != WL_SHM_FORMAT_ARGB8888)
|
|
return;
|
|
|
|
assert(pointer_pnode);
|
|
|
|
weston_paint_node_move_to_plane(pointer_pnode, &output->cursor_plane);
|
|
|
|
output->cursor_surface = view->surface;
|
|
}
|
|
|
|
static void
|
|
vnc_region32_to_region16(pixman_region16_t *dst, pixman_region32_t *src)
|
|
{
|
|
struct pixman_box32 *src_rects;
|
|
struct pixman_box16 *dest_rects;
|
|
int n_rects = 0;
|
|
int i;
|
|
|
|
src_rects = pixman_region32_rectangles(src, &n_rects);
|
|
if (!n_rects)
|
|
return;
|
|
|
|
dest_rects = xcalloc(n_rects, sizeof(*dest_rects));
|
|
|
|
for (i = 0; i < n_rects; i++) {
|
|
dest_rects[i].x1 = src_rects[i].x1;
|
|
dest_rects[i].y1 = src_rects[i].y1;
|
|
dest_rects[i].x2 = src_rects[i].x2;
|
|
dest_rects[i].y2 = src_rects[i].y2;
|
|
}
|
|
|
|
pixman_region_init_rects(dst, dest_rects, n_rects);
|
|
free(dest_rects);
|
|
}
|
|
|
|
static void
|
|
vnc_log_scope_print_region(struct weston_log_scope *log, pixman_region32_t *region)
|
|
{
|
|
struct pixman_box32 *rects;
|
|
int n_rects = 0;
|
|
int i;
|
|
|
|
rects = pixman_region32_rectangles(region, &n_rects);
|
|
if (!n_rects) {
|
|
weston_log_scope_printf(log, " empty");
|
|
return;
|
|
}
|
|
|
|
for (i = 0; i < n_rects; i++) {
|
|
int width = rects[i].x2 - rects[i].x1;
|
|
int height = rects[i].y2 - rects[i].y1;
|
|
|
|
weston_log_scope_printf(log, " %dx%d(%d,%d)", width, height,
|
|
rects[i].x1, rects[i].y1);
|
|
}
|
|
}
|
|
|
|
static void
|
|
vnc_log_damage(struct vnc_backend *backend, pixman_region32_t *buffer_damage,
|
|
pixman_region32_t *update_damage)
|
|
{
|
|
char timestr[128];
|
|
|
|
if (!weston_log_scope_is_enabled(backend->debug))
|
|
return;
|
|
|
|
weston_log_scope_timestamp(backend->debug, timestr, sizeof timestr);
|
|
|
|
weston_log_scope_printf(backend->debug, "%s buffer damage:", timestr);
|
|
vnc_log_scope_print_region(backend->debug, buffer_damage);
|
|
weston_log_scope_printf(backend->debug, "\n");
|
|
|
|
weston_log_scope_printf(backend->debug, "%s update damage:", timestr);
|
|
vnc_log_scope_print_region(backend->debug, update_damage);
|
|
weston_log_scope_printf(backend->debug, "\n\n");
|
|
}
|
|
|
|
static void
|
|
vnc_update_buffer(struct nvnc_display *display, struct pixman_region32 *damage)
|
|
{
|
|
struct nvnc *server = nvnc_display_get_server(display);
|
|
struct vnc_backend *backend = nvnc_get_userdata(server);
|
|
struct vnc_output *output = backend->output;
|
|
struct weston_compositor *ec = output->base.compositor;
|
|
struct weston_renderbuffer *renderbuffer;
|
|
pixman_region32_t local_damage;
|
|
pixman_region16_t nvnc_damage;
|
|
struct nvnc_fb *fb;
|
|
|
|
fb = nvnc_fb_pool_acquire(output->fb_pool);
|
|
assert(fb);
|
|
|
|
renderbuffer = nvnc_get_userdata(fb);
|
|
if (!renderbuffer) {
|
|
const struct pixel_format_info *pfmt;
|
|
|
|
pfmt = pixel_format_get_info(DRM_FORMAT_XRGB8888);
|
|
|
|
switch (ec->renderer->type) {
|
|
case WESTON_RENDERER_PIXMAN: {
|
|
const struct pixman_renderer_interface *pixman;
|
|
|
|
pixman = ec->renderer->pixman;
|
|
|
|
renderbuffer =
|
|
pixman->create_image_from_ptr(&output->base, pfmt,
|
|
output->base.width,
|
|
output->base.height,
|
|
nvnc_fb_get_addr(fb),
|
|
output->base.width * 4);
|
|
break;
|
|
}
|
|
case WESTON_RENDERER_GL: {
|
|
renderbuffer =
|
|
ec->renderer->gl->create_fbo(&output->base, pfmt,
|
|
output->base.width,
|
|
output->base.height,
|
|
nvnc_fb_get_addr(fb));
|
|
break;
|
|
}
|
|
default:
|
|
unreachable("cannot have auto renderer at runtime");
|
|
}
|
|
|
|
/* This is a new buffer, so the whole surface is damaged. */
|
|
pixman_region32_copy(&renderbuffer->damage,
|
|
&output->base.region);
|
|
|
|
nvnc_set_userdata(fb, renderbuffer,
|
|
(nvnc_cleanup_fn)weston_renderbuffer_unref);
|
|
}
|
|
|
|
vnc_log_damage(backend, &renderbuffer->damage, damage);
|
|
|
|
ec->renderer->repaint_output(&output->base, damage, renderbuffer);
|
|
|
|
/* Convert to local coordinates */
|
|
pixman_region32_init(&local_damage);
|
|
weston_region_global_to_output(&local_damage, &output->base, damage);
|
|
|
|
/* Convert to 16-bit */
|
|
pixman_region_init(&nvnc_damage);
|
|
vnc_region32_to_region16(&nvnc_damage, &local_damage);
|
|
|
|
nvnc_display_feed_buffer(output->display, fb, &nvnc_damage);
|
|
nvnc_fb_unref(fb);
|
|
pixman_region32_fini(&local_damage);
|
|
pixman_region_fini(&nvnc_damage);
|
|
}
|
|
|
|
static void
|
|
vnc_new_client(struct nvnc_client *client)
|
|
{
|
|
struct nvnc *server = nvnc_client_get_server(client);
|
|
struct vnc_backend *backend = nvnc_get_userdata(server);
|
|
struct vnc_output *output = backend->output;
|
|
struct vnc_peer *peer;
|
|
const char *seat_name = "VNC Client";
|
|
|
|
weston_log("New VNC client connected\n");
|
|
|
|
peer = xzalloc(sizeof(*peer));
|
|
peer->client = client;
|
|
peer->backend = backend;
|
|
peer->seat = xzalloc(sizeof(*peer->seat));
|
|
|
|
weston_seat_init(peer->seat, backend->compositor, seat_name);
|
|
weston_seat_init_pointer(peer->seat);
|
|
weston_seat_init_keyboard(peer->seat, backend->xkb_keymap);
|
|
|
|
if (wl_list_empty(&output->peers))
|
|
weston_output_power_on(&output->base);
|
|
|
|
wl_list_insert(&output->peers, &peer->link);
|
|
|
|
nvnc_set_userdata(client, peer, NULL);
|
|
nvnc_set_client_cleanup_fn(client, vnc_client_cleanup);
|
|
|
|
/*
|
|
* Make up for repaints that were skipped when no clients were
|
|
* connected.
|
|
*/
|
|
weston_output_schedule_repaint(&output->base);
|
|
}
|
|
|
|
static int
|
|
finish_frame_handler(void *data)
|
|
{
|
|
struct vnc_output *output = data;
|
|
|
|
weston_output_finish_frame_from_timer(&output->base);
|
|
|
|
return 1;
|
|
}
|
|
|
|
static int
|
|
vnc_output_enable(struct weston_output *base)
|
|
{
|
|
struct weston_renderer *renderer = base->compositor->renderer;
|
|
struct vnc_output *output = to_vnc_output(base);
|
|
struct vnc_backend *backend;
|
|
struct wl_event_loop *loop;
|
|
|
|
assert(output);
|
|
|
|
backend = output->backend;
|
|
backend->output = output;
|
|
|
|
weston_plane_init(&output->cursor_plane, backend->compositor);
|
|
|
|
switch (renderer->type) {
|
|
case WESTON_RENDERER_PIXMAN: {
|
|
const struct pixman_renderer_output_options options = {
|
|
.fb_size = {
|
|
.width = output->base.width,
|
|
.height = output->base.height,
|
|
},
|
|
.format = backend->formats[0],
|
|
};
|
|
if (renderer->pixman->output_create(&output->base, &options) < 0)
|
|
return -1;
|
|
break;
|
|
}
|
|
case WESTON_RENDERER_GL: {
|
|
const struct gl_renderer_fbo_options options = {
|
|
.area = {
|
|
.width = output->base.width,
|
|
.height = output->base.height,
|
|
},
|
|
.fb_size = {
|
|
.width = output->base.width,
|
|
.height = output->base.height,
|
|
},
|
|
};
|
|
if (renderer->gl->output_fbo_create(&output->base, &options) < 0)
|
|
return -1;
|
|
break;
|
|
}
|
|
default:
|
|
unreachable("cannot have auto renderer at runtime");
|
|
}
|
|
|
|
loop = wl_display_get_event_loop(backend->compositor->wl_display);
|
|
output->finish_frame_timer = wl_event_loop_add_timer(loop,
|
|
finish_frame_handler,
|
|
output);
|
|
|
|
output->fb_pool = nvnc_fb_pool_new(output->base.width,
|
|
output->base.height,
|
|
backend->formats[0]->format,
|
|
output->base.width);
|
|
|
|
output->display = nvnc_display_new(0, 0);
|
|
|
|
nvnc_add_display(backend->server, output->display);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
vnc_output_disable(struct weston_output *base)
|
|
{
|
|
struct weston_renderer *renderer = base->compositor->renderer;
|
|
struct vnc_output *output = to_vnc_output(base);
|
|
struct vnc_backend *backend;
|
|
|
|
assert(output);
|
|
|
|
backend = output->backend;
|
|
|
|
if (!output->base.enabled)
|
|
return 0;
|
|
|
|
nvnc_remove_display(backend->server, output->display);
|
|
nvnc_display_unref(output->display);
|
|
nvnc_fb_pool_unref(output->fb_pool);
|
|
|
|
switch (renderer->type) {
|
|
case WESTON_RENDERER_PIXMAN:
|
|
renderer->pixman->output_destroy(&output->base);
|
|
break;
|
|
case WESTON_RENDERER_GL:
|
|
renderer->gl->output_destroy(&output->base);
|
|
break;
|
|
default:
|
|
unreachable("cannot have auto renderer at runtime");
|
|
}
|
|
|
|
wl_event_source_remove(output->finish_frame_timer);
|
|
backend->output = NULL;
|
|
|
|
weston_plane_release(&output->cursor_plane);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void
|
|
vnc_output_destroy(struct weston_output *base)
|
|
{
|
|
struct vnc_output *output = to_vnc_output(base);
|
|
|
|
/* Can only be called on outputs created by vnc_create_output() */
|
|
assert(output);
|
|
|
|
vnc_output_disable(&output->base);
|
|
weston_output_release(&output->base);
|
|
|
|
free(output);
|
|
}
|
|
|
|
static struct weston_output *
|
|
vnc_create_output(struct weston_backend *backend, const char *name)
|
|
{
|
|
struct vnc_backend *b = container_of(backend, struct vnc_backend, base);
|
|
struct vnc_output *output;
|
|
|
|
output = zalloc(sizeof *output);
|
|
if (output == NULL)
|
|
return NULL;
|
|
|
|
weston_output_init(&output->base, b->compositor, name);
|
|
|
|
output->base.destroy = vnc_output_destroy;
|
|
output->base.disable = vnc_output_disable;
|
|
output->base.enable = vnc_output_enable;
|
|
output->base.attach_head = NULL;
|
|
|
|
output->backend = b;
|
|
|
|
weston_compositor_add_pending_output(&output->base, b->compositor);
|
|
|
|
return &output->base;
|
|
}
|
|
|
|
static void
|
|
vnc_destroy(struct weston_backend *base)
|
|
{
|
|
struct vnc_backend *backend = container_of(base, struct vnc_backend, base);
|
|
struct weston_compositor *ec = backend->compositor;
|
|
struct weston_head *head, *next;
|
|
|
|
nvnc_close(backend->server);
|
|
|
|
wl_list_remove(&backend->base.link);
|
|
|
|
wl_event_source_remove(backend->aml_event);
|
|
|
|
aml_unref(backend->aml);
|
|
|
|
wl_list_for_each_safe(head, next, &ec->head_list, compositor_link)
|
|
vnc_head_destroy(head);
|
|
|
|
xkb_keymap_unref(backend->xkb_keymap);
|
|
|
|
if (backend->debug)
|
|
weston_log_scope_destroy(backend->debug);
|
|
|
|
free(backend);
|
|
}
|
|
|
|
static void
|
|
vnc_head_create(struct vnc_backend *backend, const char *name)
|
|
{
|
|
struct vnc_head *head;
|
|
|
|
head = xzalloc(sizeof *head);
|
|
|
|
weston_head_init(&head->base, name);
|
|
weston_head_set_monitor_strings(&head->base, "weston", "vnc", NULL);
|
|
weston_head_set_physical_size(&head->base, 0, 0);
|
|
|
|
head->base.backend = &backend->base;
|
|
|
|
weston_head_set_connection_status(&head->base, true);
|
|
weston_compositor_add_head(backend->compositor, &head->base);
|
|
}
|
|
|
|
static void
|
|
vnc_head_destroy(struct weston_head *base)
|
|
{
|
|
struct vnc_head *head = to_vnc_head(base);
|
|
|
|
if (!head)
|
|
return;
|
|
|
|
weston_head_release(&head->base);
|
|
free(head);
|
|
}
|
|
|
|
static int
|
|
vnc_output_start_repaint_loop(struct weston_output *output)
|
|
{
|
|
struct timespec ts;
|
|
|
|
weston_compositor_read_presentation_clock(output->compositor, &ts);
|
|
weston_output_finish_frame(output, &ts, WP_PRESENTATION_FEEDBACK_INVALID);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
vnc_output_repaint(struct weston_output *base)
|
|
{
|
|
struct vnc_output *output = to_vnc_output(base);
|
|
struct vnc_backend *backend = output->backend;
|
|
pixman_region32_t damage;
|
|
|
|
assert(output);
|
|
|
|
if (wl_list_empty(&output->peers))
|
|
weston_output_power_off(base);
|
|
|
|
vnc_output_update_cursor(output);
|
|
|
|
pixman_region32_init(&damage);
|
|
|
|
weston_output_flush_damage_for_primary_plane(base, &damage);
|
|
|
|
if (pixman_region32_not_empty(&damage)) {
|
|
vnc_update_buffer(output->display, &damage);
|
|
}
|
|
|
|
pixman_region32_fini(&damage);
|
|
|
|
/*
|
|
* Make sure damage of this (or previous) damage is handled
|
|
*
|
|
* This will usually invoke the render callback where the (pixman)
|
|
* renderer gets invoked
|
|
*/
|
|
aml_dispatch(backend->aml);
|
|
|
|
weston_output_arm_frame_timer(base, output->finish_frame_timer);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static bool
|
|
vnc_clients_support_cursor(struct vnc_output *output)
|
|
{
|
|
struct vnc_peer *peer;
|
|
|
|
wl_list_for_each(peer, &output->peers, link) {
|
|
if (!nvnc_client_supports_cursor(peer->client))
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
static void
|
|
vnc_output_assign_planes(struct weston_output *base)
|
|
{
|
|
struct vnc_output *output = to_vnc_output(base);
|
|
|
|
assert(output);
|
|
|
|
if (wl_list_empty(&output->peers))
|
|
return;
|
|
|
|
/* Update VNC cursor and move cursor view to plane */
|
|
if (vnc_clients_support_cursor(output))
|
|
vnc_output_assign_cursor_plane(output);
|
|
}
|
|
|
|
static int
|
|
vnc_switch_mode(struct weston_output *base, struct weston_mode *target_mode)
|
|
{
|
|
struct vnc_output *output = to_vnc_output(base);
|
|
struct weston_size fb_size;
|
|
|
|
assert(output);
|
|
|
|
weston_output_set_single_mode(base, target_mode);
|
|
|
|
fb_size.width = target_mode->width;
|
|
fb_size.height = target_mode->height;
|
|
|
|
weston_renderer_resize_output(base, &fb_size, NULL);
|
|
|
|
nvnc_fb_pool_resize(output->fb_pool, target_mode->width,
|
|
target_mode->height, DRM_FORMAT_XRGB8888,
|
|
target_mode->width);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
vnc_output_set_size(struct weston_output *base, int width, int height,
|
|
bool resizeable)
|
|
{
|
|
struct vnc_output *output = to_vnc_output(base);
|
|
struct vnc_backend *backend = output->backend;
|
|
struct weston_mode init_mode;
|
|
|
|
/* We can only be called once. */
|
|
assert(!output->base.current_mode);
|
|
|
|
wl_list_init(&output->peers);
|
|
|
|
init_mode.width = width;
|
|
init_mode.height = height;
|
|
init_mode.refresh = backend->vnc_monitor_refresh_rate;
|
|
|
|
weston_output_set_single_mode(base, &init_mode);
|
|
|
|
output->base.start_repaint_loop = vnc_output_start_repaint_loop;
|
|
output->base.repaint = vnc_output_repaint;
|
|
output->base.assign_planes = vnc_output_assign_planes;
|
|
output->base.set_backlight = NULL;
|
|
output->base.set_dpms = NULL;
|
|
output->base.switch_mode = vnc_switch_mode;
|
|
|
|
output->resizeable = resizeable;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static const struct weston_vnc_output_api api = {
|
|
vnc_output_set_size,
|
|
};
|
|
|
|
static int
|
|
vnc_aml_dispatch(int fd, uint32_t mask, void *data)
|
|
{
|
|
struct aml *aml = data;
|
|
|
|
aml_poll(aml, 0);
|
|
aml_dispatch(aml);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static struct vnc_backend *
|
|
vnc_backend_create(struct weston_compositor *compositor,
|
|
struct weston_vnc_backend_config *config)
|
|
{
|
|
struct vnc_backend *backend;
|
|
struct wl_event_loop *loop;
|
|
struct weston_head *base, *next;
|
|
int ret;
|
|
int fd;
|
|
|
|
backend = zalloc(sizeof *backend);
|
|
if (backend == NULL)
|
|
return NULL;
|
|
|
|
wl_list_init(&backend->base.link);
|
|
|
|
backend->compositor = compositor;
|
|
backend->base.destroy = vnc_destroy;
|
|
backend->base.create_output = vnc_create_output;
|
|
backend->vnc_monitor_refresh_rate = config->refresh_rate * 1000;
|
|
|
|
backend->debug = weston_compositor_add_log_scope(compositor,
|
|
"vnc-backend",
|
|
"Debug messages from VNC backend\n",
|
|
NULL, NULL, NULL);
|
|
|
|
wl_list_insert(&compositor->backend_list, &backend->base.link);
|
|
|
|
backend->base.supported_presentation_clocks =
|
|
WESTON_PRESENTATION_CLOCKS_SOFTWARE;
|
|
|
|
backend->formats_count = ARRAY_LENGTH(vnc_formats);
|
|
backend->formats = pixel_format_get_array(vnc_formats,
|
|
backend->formats_count);
|
|
if (!compositor->renderer) {
|
|
switch (config->renderer) {
|
|
case WESTON_RENDERER_AUTO:
|
|
case WESTON_RENDERER_PIXMAN:
|
|
if (weston_compositor_init_renderer(compositor,
|
|
WESTON_RENDERER_PIXMAN,
|
|
NULL) < 0)
|
|
goto err_compositor;
|
|
break;
|
|
case WESTON_RENDERER_GL: {
|
|
const struct gl_renderer_display_options options = {
|
|
.egl_platform = EGL_PLATFORM_SURFACELESS_MESA,
|
|
.formats = backend->formats,
|
|
.formats_count = backend->formats_count,
|
|
};
|
|
if (weston_compositor_init_renderer(compositor,
|
|
WESTON_RENDERER_GL,
|
|
&options.base) < 0)
|
|
goto err_compositor;
|
|
break;
|
|
}
|
|
default:
|
|
weston_log("Unsupported renderer requested\n");
|
|
goto err_compositor;
|
|
}
|
|
}
|
|
|
|
vnc_head_create(backend, "vnc");
|
|
|
|
compositor->capabilities |= WESTON_CAP_ARBITRARY_MODES;
|
|
|
|
backend->xkb_rule_name.rules = strdup(compositor->xkb_names.rules);
|
|
backend->xkb_rule_name.model = strdup(compositor->xkb_names.model);
|
|
backend->xkb_rule_name.layout = strdup(compositor->xkb_names.layout);
|
|
|
|
backend->xkb_keymap = xkb_keymap_new_from_names(
|
|
backend->compositor->xkb_context,
|
|
&backend->xkb_rule_name, 0);
|
|
|
|
loop = wl_display_get_event_loop(backend->compositor->wl_display);
|
|
|
|
backend->aml = aml_new();
|
|
if (!backend->aml)
|
|
goto err_output;
|
|
aml_set_default(backend->aml);
|
|
|
|
fd = aml_get_fd(backend->aml);
|
|
|
|
backend->aml_event = wl_event_loop_add_fd(loop, fd, WL_EVENT_READABLE,
|
|
vnc_aml_dispatch,
|
|
backend->aml);
|
|
|
|
backend->server = nvnc_open(config->bind_address, config->port);
|
|
if (!backend->server)
|
|
goto err_output;
|
|
|
|
nvnc_set_new_client_fn(backend->server, vnc_new_client);
|
|
nvnc_set_pointer_fn(backend->server, vnc_pointer_event);
|
|
nvnc_set_key_fn(backend->server, vnc_handle_key_event);
|
|
nvnc_set_key_code_fn(backend->server, vnc_handle_key_code_event);
|
|
nvnc_set_desktop_layout_fn(backend->server, vnc_handle_desktop_layout_event);
|
|
nvnc_set_userdata(backend->server, backend, NULL);
|
|
nvnc_set_name(backend->server, "Weston VNC backend");
|
|
|
|
if (!config->disable_tls) {
|
|
if (!nvnc_has_auth()) {
|
|
weston_log("Neat VNC built without TLS support\n");
|
|
goto err_output;
|
|
}
|
|
if (!config->server_cert && !config->server_key) {
|
|
weston_log(
|
|
"The VNC backend requires a key and a "
|
|
"certificate for TLS security"
|
|
" (--vnc-tls-cert/--vnc-tls-key)\n");
|
|
goto err_output;
|
|
}
|
|
if (!config->server_cert) {
|
|
weston_log(
|
|
"Missing TLS certificate (--vnc-tls-cert)\n");
|
|
goto err_output;
|
|
}
|
|
if (!config->server_key) {
|
|
weston_log("Missing TLS key (--vnc-tls-key)\n");
|
|
goto err_output;
|
|
}
|
|
|
|
ret = nvnc_set_tls_creds(backend->server, config->server_key,
|
|
config->server_cert);
|
|
if (ret) {
|
|
weston_log("Failed set TLS credentials\n");
|
|
goto err_output;
|
|
}
|
|
|
|
ret = nvnc_enable_auth(
|
|
backend->server,
|
|
NVNC_AUTH_REQUIRE_AUTH | NVNC_AUTH_REQUIRE_ENCRYPTION,
|
|
vnc_handle_auth, NULL);
|
|
if (ret) {
|
|
weston_log("Failed to enable TLS support\n");
|
|
goto err_output;
|
|
}
|
|
|
|
weston_log("TLS support activated\n");
|
|
} else {
|
|
ret = nvnc_enable_auth(backend->server, NVNC_AUTH_REQUIRE_AUTH,
|
|
vnc_handle_auth, NULL);
|
|
if (ret) {
|
|
weston_log("Failed to enable authentication\n");
|
|
goto err_output;
|
|
}
|
|
|
|
weston_log(
|
|
"warning: VNC enabled without Transport Layer "
|
|
"Security!\n");
|
|
}
|
|
|
|
ret = weston_plugin_api_register(compositor, WESTON_VNC_OUTPUT_API_NAME,
|
|
&api, sizeof(api));
|
|
if (ret < 0) {
|
|
weston_log("Failed to register output API.\n");
|
|
goto err_output;
|
|
}
|
|
|
|
return backend;
|
|
|
|
err_output:
|
|
wl_list_for_each_safe(base, next, &compositor->head_list, compositor_link)
|
|
vnc_head_destroy(base);
|
|
err_compositor:
|
|
wl_list_remove(&backend->base.link);
|
|
free(backend);
|
|
return NULL;
|
|
}
|
|
|
|
static void
|
|
config_init_to_defaults(struct weston_vnc_backend_config *config)
|
|
{
|
|
config->bind_address = NULL;
|
|
config->port = 5900;
|
|
config->refresh_rate = VNC_DEFAULT_FREQ;
|
|
}
|
|
|
|
WL_EXPORT int
|
|
weston_backend_init(struct weston_compositor *compositor,
|
|
struct weston_backend_config *config_base)
|
|
{
|
|
struct vnc_backend *backend;
|
|
struct weston_vnc_backend_config config = {{ 0, }};
|
|
|
|
weston_log("Initializing VNC backend\n");
|
|
|
|
if (config_base == NULL ||
|
|
config_base->struct_version != WESTON_VNC_BACKEND_CONFIG_VERSION ||
|
|
config_base->struct_size > sizeof(struct weston_vnc_backend_config)) {
|
|
weston_log("VNC backend config structure is invalid\n");
|
|
return -1;
|
|
}
|
|
|
|
if (compositor->renderer) {
|
|
switch (compositor->renderer->type) {
|
|
case WESTON_RENDERER_PIXMAN:
|
|
case WESTON_RENDERER_GL:
|
|
break;
|
|
default:
|
|
weston_log("Renderer not supported by VNC backend\n");
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
config_init_to_defaults(&config);
|
|
memcpy(&config, config_base, config_base->struct_size);
|
|
|
|
backend = vnc_backend_create(compositor, &config);
|
|
if (backend == NULL)
|
|
return -1;
|
|
return 0;
|
|
}
|