weston/libweston/backend-vnc/vnc.c
Derek Foreman bbaba601c8 vnc: Fix cursor updates
Now that overlapping outputs are a thing, we have a problem with vnc
cursors.

The surface->damage used to update the vnc cursor might actually be
flushed by a previous output's repaint cycle, leading to a missing cursor
update to the vnc client.

Instead we should use the damage accumulated on the cursor plane to choose
when to update the cursor. This damage is in output coordinates, so let's
be lazy and just use the presence of damage as an indicator that the
cursor needs an update.

Signed-off-by: Derek Foreman <derek.foreman@collabora.com>
2023-11-10 09:38:35 -06:00

1327 lines
34 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 (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;
int32_t stride;
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;
stride = wl_shm_buffer_get_stride(buffer->shm_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 * 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_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_shutdown(struct weston_backend *base)
{
struct vnc_backend *backend = container_of(base, struct vnc_backend, base);
nvnc_close(backend->server);
}
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;
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, pixman_region32_t *damage)
{
struct vnc_output *output = to_vnc_output(base);
struct vnc_backend *backend = output->backend;
assert(output);
if (wl_list_empty(&output->peers))
weston_output_power_off(base);
vnc_output_update_cursor(output);
if (pixman_region32_not_empty(damage)) {
vnc_update_buffer(output->display, 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.shutdown = vnc_shutdown;
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 (!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");
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;
}