2019-09-07 16:51:07 +00:00
|
|
|
/*
|
|
|
|
* Copyright (c) 2019 Andri Yngvason
|
|
|
|
*
|
|
|
|
* Permission to use, copy, modify, and/or distribute this software for any
|
|
|
|
* purpose with or without fee is hereby granted, provided that the above
|
|
|
|
* copyright notice and this permission notice appear in all copies.
|
|
|
|
*
|
|
|
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
|
|
|
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
|
|
|
* AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
|
|
|
* INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
|
|
|
* LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
|
|
|
|
* OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
|
|
|
* PERFORMANCE OF THIS SOFTWARE.
|
|
|
|
*/
|
|
|
|
|
2019-08-12 20:49:23 +00:00
|
|
|
#include "rfb-proto.h"
|
2019-08-25 16:40:59 +00:00
|
|
|
#include "util.h"
|
2019-08-25 19:10:35 +00:00
|
|
|
#include "zrle.h"
|
2019-09-19 18:14:24 +00:00
|
|
|
#include "raw-encoding.h"
|
2019-08-27 21:49:28 +00:00
|
|
|
#include "vec.h"
|
2019-08-31 23:16:55 +00:00
|
|
|
#include "type-macros.h"
|
2019-10-07 17:39:54 +00:00
|
|
|
#include "fb.h"
|
2019-08-28 22:46:47 +00:00
|
|
|
#include "neatvnc.h"
|
2019-08-12 20:49:23 +00:00
|
|
|
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
#include <sys/queue.h>
|
|
|
|
#include <assert.h>
|
|
|
|
#include <uv.h>
|
|
|
|
#include <libdrm/drm_fourcc.h>
|
2019-08-25 19:10:35 +00:00
|
|
|
#include <pixman.h>
|
2019-09-15 21:51:07 +00:00
|
|
|
#include <pthread.h>
|
2019-08-12 20:49:23 +00:00
|
|
|
|
2019-09-10 18:17:45 +00:00
|
|
|
#ifndef DRM_FORMAT_INVALID
|
|
|
|
#define DRM_FORMAT_INVALID 0
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifndef DRM_FORMAT_MOD_LINEAR
|
|
|
|
#define DRM_FORMAT_MOD_LINEAR DRM_FORMAT_MOD_NONE
|
|
|
|
#endif
|
|
|
|
|
2019-08-29 21:47:02 +00:00
|
|
|
#define DEFAULT_NAME "Neat VNC"
|
2019-08-12 20:49:23 +00:00
|
|
|
#define READ_BUFFER_SIZE 4096
|
|
|
|
#define MSG_BUFFER_SIZE 4096
|
|
|
|
|
2019-09-19 18:14:24 +00:00
|
|
|
#define MAX_ENCODINGS 32
|
2019-08-28 22:46:47 +00:00
|
|
|
|
2019-09-19 18:14:24 +00:00
|
|
|
#define EXPORT __attribute__((visibility("default")))
|
2019-08-12 23:33:06 +00:00
|
|
|
|
2019-08-31 23:16:55 +00:00
|
|
|
enum nvnc_client_state {
|
2019-08-12 20:49:23 +00:00
|
|
|
VNC_CLIENT_STATE_ERROR = -1,
|
|
|
|
VNC_CLIENT_STATE_WAITING_FOR_VERSION = 0,
|
|
|
|
VNC_CLIENT_STATE_WAITING_FOR_SECURITY,
|
|
|
|
VNC_CLIENT_STATE_WAITING_FOR_INIT,
|
|
|
|
VNC_CLIENT_STATE_READY,
|
|
|
|
};
|
|
|
|
|
2019-08-28 22:46:47 +00:00
|
|
|
struct nvnc;
|
2019-08-12 20:49:23 +00:00
|
|
|
|
2019-08-31 23:16:55 +00:00
|
|
|
struct nvnc_common {
|
|
|
|
void* userdata;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct nvnc_client {
|
|
|
|
struct nvnc_common common;
|
2019-09-18 21:28:48 +00:00
|
|
|
int ref;
|
2019-08-12 22:11:41 +00:00
|
|
|
uv_tcp_t stream_handle;
|
2019-08-28 22:46:47 +00:00
|
|
|
struct nvnc *server;
|
2019-08-31 23:16:55 +00:00
|
|
|
enum nvnc_client_state state;
|
2019-08-25 19:10:35 +00:00
|
|
|
uint32_t fourcc;
|
|
|
|
struct rfb_pixel_format pixfmt;
|
2019-09-19 18:14:24 +00:00
|
|
|
enum rfb_encodings encodings[MAX_ENCODINGS + 1];
|
|
|
|
size_t n_encodings;
|
2019-08-31 23:16:55 +00:00
|
|
|
LIST_ENTRY(nvnc_client) link;
|
2019-08-28 22:46:47 +00:00
|
|
|
struct pixman_region16 requested_region;
|
2019-08-31 23:30:08 +00:00
|
|
|
nvnc_client_fn cleanup_fn;
|
2019-09-01 18:40:11 +00:00
|
|
|
z_stream z_stream;
|
2019-08-12 20:49:23 +00:00
|
|
|
size_t buffer_index;
|
|
|
|
size_t buffer_len;
|
|
|
|
uint8_t msg_buffer[MSG_BUFFER_SIZE];
|
|
|
|
};
|
|
|
|
|
2019-08-31 23:16:55 +00:00
|
|
|
LIST_HEAD(nvnc_client_list, nvnc_client);
|
2019-08-12 20:49:23 +00:00
|
|
|
|
|
|
|
struct vnc_display {
|
|
|
|
uint16_t width;
|
|
|
|
uint16_t height;
|
|
|
|
uint32_t pixfmt; /* fourcc pixel format */
|
2019-08-29 21:47:02 +00:00
|
|
|
char name[256];
|
2019-08-12 20:49:23 +00:00
|
|
|
};
|
|
|
|
|
2019-08-28 22:46:47 +00:00
|
|
|
struct nvnc {
|
2019-08-31 23:16:55 +00:00
|
|
|
struct nvnc_common common;
|
2019-08-12 20:49:23 +00:00
|
|
|
uv_tcp_t tcp_handle;
|
2019-08-31 23:16:55 +00:00
|
|
|
struct nvnc_client_list clients;
|
2019-08-12 20:49:23 +00:00
|
|
|
struct vnc_display display;
|
2019-08-28 22:46:47 +00:00
|
|
|
void *userdata;
|
2019-09-15 21:51:07 +00:00
|
|
|
int n_pending_updates;
|
2019-08-28 22:46:47 +00:00
|
|
|
nvnc_key_fn key_fn;
|
|
|
|
nvnc_pointer_fn pointer_fn;
|
|
|
|
nvnc_fb_req_fn fb_req_fn;
|
2019-08-31 23:30:08 +00:00
|
|
|
nvnc_client_fn new_client_fn;
|
2019-08-12 20:49:23 +00:00
|
|
|
};
|
|
|
|
|
2019-09-15 21:51:07 +00:00
|
|
|
struct fb_update_work {
|
|
|
|
uv_work_t work;
|
|
|
|
struct nvnc_client *client;
|
|
|
|
struct pixman_region16 region;
|
|
|
|
struct rfb_pixel_format server_fmt;
|
|
|
|
struct vec frame;
|
|
|
|
const struct nvnc_fb *fb;
|
|
|
|
nvnc_update_done_fn on_done;
|
|
|
|
};
|
|
|
|
|
2019-08-12 22:11:41 +00:00
|
|
|
static const char* fourcc_to_string(uint32_t fourcc)
|
|
|
|
{
|
|
|
|
static char buffer[5];
|
|
|
|
|
|
|
|
buffer[0] = (fourcc >> 0) & 0xff;
|
|
|
|
buffer[1] = (fourcc >> 8) & 0xff;
|
|
|
|
buffer[2] = (fourcc >> 16) & 0xff;
|
|
|
|
buffer[3] = (fourcc >> 24) & 0xff;
|
|
|
|
buffer[4] = '\0';
|
|
|
|
|
|
|
|
return buffer;
|
|
|
|
}
|
|
|
|
|
2019-08-12 20:49:23 +00:00
|
|
|
static void allocate_read_buffer(uv_handle_t *handle, size_t suggested_size,
|
|
|
|
uv_buf_t *buf)
|
|
|
|
{
|
|
|
|
(void)suggested_size;
|
|
|
|
|
|
|
|
buf->base = malloc(READ_BUFFER_SIZE);
|
|
|
|
buf->len = buf->base ? READ_BUFFER_SIZE : 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void cleanup_client(uv_handle_t* handle)
|
|
|
|
{
|
2019-08-31 23:16:55 +00:00
|
|
|
struct nvnc_client *client =
|
|
|
|
container_of((uv_tcp_t*)handle, struct nvnc_client,
|
|
|
|
stream_handle);
|
2019-08-12 20:49:23 +00:00
|
|
|
|
2019-08-31 23:30:08 +00:00
|
|
|
nvnc_client_fn fn = client->cleanup_fn;
|
|
|
|
if (fn)
|
|
|
|
fn(client);
|
|
|
|
|
2019-09-01 18:40:11 +00:00
|
|
|
deflateEnd(&client->z_stream);
|
|
|
|
|
2019-08-12 20:49:23 +00:00
|
|
|
LIST_REMOVE(client, link);
|
2019-08-28 22:46:47 +00:00
|
|
|
pixman_region_fini(&client->requested_region);
|
2019-08-12 20:49:23 +00:00
|
|
|
free(client);
|
|
|
|
}
|
|
|
|
|
2019-09-18 21:28:48 +00:00
|
|
|
static inline void client_close(struct nvnc_client *client)
|
|
|
|
{
|
|
|
|
uv_close((uv_handle_t*)&client->stream_handle, cleanup_client);
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline void client_unref(struct nvnc_client *client)
|
|
|
|
{
|
|
|
|
if (--client->ref == 0)
|
|
|
|
client_close(client);
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline void client_ref(struct nvnc_client *client)
|
|
|
|
{
|
|
|
|
++client->ref;
|
|
|
|
}
|
|
|
|
|
2019-08-12 20:49:23 +00:00
|
|
|
static void close_after_write(uv_write_t *req, int status)
|
|
|
|
{
|
2019-09-18 21:28:48 +00:00
|
|
|
struct nvnc_client* client =
|
|
|
|
container_of((uv_tcp_t*)req->handle, struct nvnc_client,
|
|
|
|
stream_handle);
|
|
|
|
|
|
|
|
client_unref(client);
|
2019-08-12 20:49:23 +00:00
|
|
|
}
|
|
|
|
|
2019-08-31 23:16:55 +00:00
|
|
|
static int handle_unsupported_version(struct nvnc_client *client)
|
2019-08-12 20:49:23 +00:00
|
|
|
{
|
|
|
|
char buffer[256];
|
|
|
|
|
|
|
|
client->state = VNC_CLIENT_STATE_ERROR;
|
|
|
|
|
|
|
|
struct rfb_error_reason *reason =
|
|
|
|
(struct rfb_error_reason*)(buffer + 1);
|
|
|
|
|
|
|
|
static const char reason_string[] = "Unsupported version\n";
|
|
|
|
|
|
|
|
buffer[0] = 0; /* Number of security types is 0 on error */
|
|
|
|
reason->length = htonl(strlen(reason_string));
|
|
|
|
(void)strcmp(reason->message, reason_string);
|
|
|
|
|
2019-08-12 22:11:41 +00:00
|
|
|
vnc__write((uv_stream_t*)&client->stream_handle, buffer,
|
2019-08-12 20:49:23 +00:00
|
|
|
1 + sizeof(*reason) + strlen(reason_string),
|
|
|
|
close_after_write);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2019-08-31 23:16:55 +00:00
|
|
|
static int on_version_message(struct nvnc_client *client)
|
2019-08-12 20:49:23 +00:00
|
|
|
{
|
|
|
|
if (client->buffer_len - client->buffer_index < 12)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
char version_string[13];
|
|
|
|
memcpy(version_string, client->msg_buffer + client->buffer_index, 12);
|
|
|
|
version_string[12] = '\0';
|
|
|
|
|
|
|
|
if (strcmp(RFB_VERSION_MESSAGE, version_string) != 0)
|
|
|
|
return handle_unsupported_version(client);
|
|
|
|
|
|
|
|
const static struct rfb_security_types_msg security = {
|
|
|
|
.n = 1,
|
|
|
|
.types = {
|
|
|
|
RFB_SECURITY_TYPE_NONE,
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
2019-08-12 22:11:41 +00:00
|
|
|
vnc__write((uv_stream_t*)&client->stream_handle, &security, sizeof(security), NULL);
|
2019-08-12 20:49:23 +00:00
|
|
|
|
|
|
|
client->state = VNC_CLIENT_STATE_WAITING_FOR_SECURITY;
|
|
|
|
return 12;
|
|
|
|
}
|
|
|
|
|
2019-08-31 23:16:55 +00:00
|
|
|
static int handle_invalid_security_type(struct nvnc_client *client)
|
2019-08-12 20:49:23 +00:00
|
|
|
{
|
|
|
|
char buffer[256];
|
|
|
|
|
|
|
|
client->state = VNC_CLIENT_STATE_ERROR;
|
|
|
|
|
2019-08-12 22:11:41 +00:00
|
|
|
uint8_t *result = (uint8_t*)buffer;
|
2019-08-12 20:49:23 +00:00
|
|
|
|
|
|
|
struct rfb_error_reason *reason =
|
|
|
|
(struct rfb_error_reason*)(buffer + sizeof(*result));
|
|
|
|
|
|
|
|
static const char reason_string[] = "Unsupported version\n";
|
|
|
|
|
|
|
|
*result = htonl(RFB_SECURITY_HANDSHAKE_FAILED);
|
|
|
|
reason->length = htonl(strlen(reason_string));
|
|
|
|
(void)strcmp(reason->message, reason_string);
|
|
|
|
|
2019-08-12 22:11:41 +00:00
|
|
|
vnc__write((uv_stream_t*)&client->stream_handle, buffer,
|
2019-08-12 20:49:23 +00:00
|
|
|
sizeof(*result) + sizeof(*reason) + strlen(reason_string),
|
|
|
|
close_after_write);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2019-08-31 23:16:55 +00:00
|
|
|
static int on_security_message(struct nvnc_client *client)
|
2019-08-12 20:49:23 +00:00
|
|
|
{
|
|
|
|
if (client->buffer_len - client->buffer_index < 1)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
uint8_t type = client->msg_buffer[client->buffer_index];
|
|
|
|
|
|
|
|
if (type != RFB_SECURITY_TYPE_NONE)
|
|
|
|
return handle_invalid_security_type(client);
|
|
|
|
|
|
|
|
enum rfb_security_handshake_result result
|
|
|
|
= htonl(RFB_SECURITY_HANDSHAKE_OK);
|
|
|
|
|
2019-08-12 22:11:41 +00:00
|
|
|
vnc__write((uv_stream_t*)&client->stream_handle, &result, sizeof(result), NULL);
|
2019-08-12 20:49:23 +00:00
|
|
|
|
|
|
|
client->state = VNC_CLIENT_STATE_WAITING_FOR_INIT;
|
|
|
|
return sizeof(type);
|
|
|
|
}
|
|
|
|
|
2019-08-31 23:16:55 +00:00
|
|
|
static void disconnect_all_other_clients(struct nvnc_client *client)
|
2019-08-12 20:49:23 +00:00
|
|
|
{
|
2019-09-06 20:07:09 +00:00
|
|
|
struct nvnc_client *node;
|
|
|
|
LIST_FOREACH(node, &client->server->clients, link)
|
|
|
|
if (node != client)
|
2019-09-18 21:28:48 +00:00
|
|
|
client_unref(client);
|
2019-08-12 20:49:23 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
int rfb_pixfmt_from_fourcc(struct rfb_pixel_format *dst, uint32_t src) {
|
|
|
|
switch (src & ~DRM_FORMAT_BIG_ENDIAN) {
|
|
|
|
case DRM_FORMAT_RGBA8888:
|
|
|
|
case DRM_FORMAT_RGBX8888:
|
|
|
|
dst->red_shift = 24;
|
|
|
|
dst->green_shift = 16;
|
|
|
|
dst->blue_shift = 8;
|
|
|
|
goto bpp_32;
|
|
|
|
case DRM_FORMAT_BGRA8888:
|
|
|
|
case DRM_FORMAT_BGRX8888:
|
|
|
|
dst->red_shift = 8;
|
|
|
|
dst->green_shift = 16;
|
|
|
|
dst->blue_shift = 24;
|
|
|
|
goto bpp_32;
|
|
|
|
case DRM_FORMAT_ARGB8888:
|
|
|
|
case DRM_FORMAT_XRGB8888:
|
|
|
|
dst->red_shift = 0;
|
|
|
|
dst->green_shift = 8;
|
|
|
|
dst->blue_shift = 16;
|
|
|
|
goto bpp_32;
|
|
|
|
case DRM_FORMAT_ABGR8888:
|
|
|
|
case DRM_FORMAT_XBGR8888:
|
|
|
|
dst->red_shift = 16;
|
|
|
|
dst->green_shift = 8;
|
|
|
|
dst->blue_shift = 0;
|
|
|
|
bpp_32:
|
|
|
|
dst->bits_per_pixel = 32;
|
|
|
|
dst->depth = 24;
|
2019-08-25 19:10:35 +00:00
|
|
|
dst->red_max = 0xff;
|
|
|
|
dst->green_max = 0xff;
|
|
|
|
dst->blue_max = 0xff;
|
2019-08-12 20:49:23 +00:00
|
|
|
break;
|
|
|
|
case DRM_FORMAT_RGBA4444:
|
|
|
|
case DRM_FORMAT_RGBX4444:
|
|
|
|
dst->red_shift = 12;
|
|
|
|
dst->green_shift = 8;
|
|
|
|
dst->blue_shift = 4;
|
|
|
|
goto bpp_16;
|
|
|
|
case DRM_FORMAT_BGRA4444:
|
|
|
|
case DRM_FORMAT_BGRX4444:
|
|
|
|
dst->red_shift = 4;
|
|
|
|
dst->green_shift = 8;
|
|
|
|
dst->blue_shift = 12;
|
|
|
|
goto bpp_16;
|
|
|
|
case DRM_FORMAT_ARGB4444:
|
|
|
|
case DRM_FORMAT_XRGB4444:
|
|
|
|
dst->red_shift = 8;
|
|
|
|
dst->green_shift = 4;
|
|
|
|
dst->blue_shift = 0;
|
|
|
|
goto bpp_16;
|
|
|
|
case DRM_FORMAT_ABGR4444:
|
|
|
|
case DRM_FORMAT_XBGR4444:
|
|
|
|
dst->red_shift = 0;
|
|
|
|
dst->green_shift = 4;
|
|
|
|
dst->blue_shift = 8;
|
|
|
|
bpp_16:
|
|
|
|
dst->bits_per_pixel = 16;
|
|
|
|
dst->depth = 12;
|
2019-08-25 19:10:35 +00:00
|
|
|
dst->red_max = 0x7f;
|
|
|
|
dst->green_max = 0x7f;
|
|
|
|
dst->blue_max = 0x7f;
|
2019-08-12 20:49:23 +00:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
dst->big_endian_flag = !!(src & DRM_FORMAT_BIG_ENDIAN);
|
|
|
|
dst->true_colour_flag = 1;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
};
|
|
|
|
|
|
|
|
static int max_values_to_depth(int r, int g, int b)
|
|
|
|
{
|
|
|
|
if (r == 5 && g == 5 && b == 3) return 8;
|
|
|
|
if (r == 15 && g == 15 && b == 15) return 12;
|
|
|
|
if (r == 31 && g == 31 && b == 31) return 15;
|
|
|
|
if (r == 31 && g == 127 && b == 31) return 16;
|
|
|
|
if (r == 255 && g == 255 && b == 255) return 24;
|
|
|
|
if (r == 1023 && g == 1023 && b == 1023) return 30;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2019-08-12 22:11:41 +00:00
|
|
|
static uint32_t shift_values_to_fourcc(int r, int g, int b, int bpp)
|
2019-08-12 20:49:23 +00:00
|
|
|
{
|
|
|
|
#define RGBEQ(rv, gv, bv) (r == (rv) && g == (gv) && b == (bv))
|
|
|
|
if (bpp == 32 && RGBEQ(24, 16, 8)) return DRM_FORMAT_RGBX8888;
|
|
|
|
if (bpp == 32 && RGBEQ( 8, 16, 24)) return DRM_FORMAT_BGRX8888;
|
|
|
|
if (bpp == 32 && RGBEQ(16, 8, 0)) return DRM_FORMAT_XRGB8888;
|
|
|
|
if (bpp == 32 && RGBEQ( 0, 8, 16)) return DRM_FORMAT_XBGR8888;
|
|
|
|
|
|
|
|
if (bpp == 32 && RGBEQ(22, 12, 2)) return DRM_FORMAT_RGBX1010102;
|
|
|
|
if (bpp == 32 && RGBEQ( 2, 12, 22)) return DRM_FORMAT_BGRX1010102;
|
|
|
|
if (bpp == 32 && RGBEQ(20, 10, 0)) return DRM_FORMAT_XRGB2101010;
|
|
|
|
if (bpp == 32 && RGBEQ( 0, 10, 20)) return DRM_FORMAT_XBGR2101010;
|
|
|
|
|
|
|
|
if (bpp == 24 && RGBEQ( 0, 8, 16)) return DRM_FORMAT_BGR888;
|
|
|
|
if (bpp == 24 && RGBEQ(16, 8, 0)) return DRM_FORMAT_RGB888;
|
|
|
|
|
|
|
|
if (bpp == 16 && RGBEQ(12, 8, 4)) return DRM_FORMAT_RGBX4444;
|
|
|
|
if (bpp == 16 && RGBEQ( 4, 8, 12)) return DRM_FORMAT_BGRX4444;
|
|
|
|
if (bpp == 16 && RGBEQ( 8, 4, 0)) return DRM_FORMAT_XRGB4444;
|
|
|
|
if (bpp == 16 && RGBEQ( 0, 4, 8)) return DRM_FORMAT_XBGR4444;
|
|
|
|
|
|
|
|
if (bpp == 16 && RGBEQ(11, 6, 1)) return DRM_FORMAT_RGBX5551;
|
|
|
|
if (bpp == 16 && RGBEQ( 1, 6, 11)) return DRM_FORMAT_BGRX5551;
|
|
|
|
if (bpp == 16 && RGBEQ(15, 5, 0)) return DRM_FORMAT_XRGB1555;
|
|
|
|
if (bpp == 16 && RGBEQ( 0, 5, 15)) return DRM_FORMAT_XBGR1555;
|
|
|
|
|
|
|
|
if (bpp == 16 && RGBEQ(11, 5, 0)) return DRM_FORMAT_RGB565;
|
|
|
|
if (bpp == 16 && RGBEQ( 0, 5, 11)) return DRM_FORMAT_BGR565;
|
|
|
|
|
|
|
|
if (bpp == 8 && RGBEQ( 5, 2, 0)) return DRM_FORMAT_RGB332;
|
|
|
|
if (bpp == 8 && RGBEQ( 0, 2, 5)) return DRM_FORMAT_BGR233;
|
|
|
|
|
|
|
|
return DRM_FORMAT_INVALID;
|
|
|
|
#undef RGBEQ
|
|
|
|
}
|
|
|
|
|
|
|
|
int get_fourcc_depth(uint32_t fourcc)
|
|
|
|
{
|
|
|
|
switch (fourcc) {
|
|
|
|
case DRM_FORMAT_RGB332:
|
|
|
|
case DRM_FORMAT_BGR233:
|
|
|
|
return 8;
|
|
|
|
default:
|
2019-08-12 22:11:41 +00:00
|
|
|
return (((fourcc >> 24) & 0xff) - '0') +
|
|
|
|
(((fourcc >> 16) & 0xff) - '0') * 10;
|
2019-08-12 20:49:23 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
uint32_t rfb_pixfmt_to_fourcc(const struct rfb_pixel_format *fmt)
|
|
|
|
{
|
|
|
|
if (!fmt->true_colour_flag)
|
|
|
|
return DRM_FORMAT_INVALID;
|
|
|
|
|
|
|
|
/* Note: The depth value given by the client is ignored */
|
2019-08-25 19:10:35 +00:00
|
|
|
int depth = max_values_to_depth(fmt->red_max, fmt->green_max,
|
|
|
|
fmt->blue_max);
|
2019-08-12 20:49:23 +00:00
|
|
|
if (depth < 0)
|
|
|
|
return DRM_FORMAT_INVALID;
|
|
|
|
|
|
|
|
uint32_t fourcc =
|
2019-08-12 22:11:41 +00:00
|
|
|
shift_values_to_fourcc(fmt->red_shift, fmt->green_shift,
|
2019-08-12 20:49:23 +00:00
|
|
|
fmt->blue_shift, fmt->bits_per_pixel);
|
|
|
|
|
|
|
|
if (fourcc == DRM_FORMAT_INVALID)
|
|
|
|
return DRM_FORMAT_INVALID;
|
|
|
|
|
|
|
|
if (get_fourcc_depth(fourcc) != depth)
|
|
|
|
return DRM_FORMAT_INVALID;
|
|
|
|
|
|
|
|
fourcc |= fmt->big_endian_flag ? DRM_FORMAT_BIG_ENDIAN : 0;
|
|
|
|
|
|
|
|
return fourcc;
|
|
|
|
}
|
|
|
|
|
2019-08-31 23:16:55 +00:00
|
|
|
static void send_server_init_message(struct nvnc_client *client)
|
2019-08-12 20:49:23 +00:00
|
|
|
{
|
2019-08-28 22:46:47 +00:00
|
|
|
struct nvnc *server = client->server;
|
2019-08-12 20:49:23 +00:00
|
|
|
struct vnc_display *display = &server->display;
|
|
|
|
|
|
|
|
size_t name_len = strlen(display->name);
|
|
|
|
size_t size = sizeof(struct rfb_server_init_msg) + name_len;
|
|
|
|
|
|
|
|
struct rfb_server_init_msg *msg = calloc(1, size);
|
|
|
|
if (!msg) {
|
2019-09-18 21:28:48 +00:00
|
|
|
client_unref(client);
|
2019-08-12 20:49:23 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
msg->width = htons(display->width),
|
|
|
|
msg->height = htons(display->height),
|
2019-08-12 22:11:41 +00:00
|
|
|
msg->name_length = htonl(name_len),
|
2019-08-12 20:49:23 +00:00
|
|
|
memcpy(msg->name_string, display->name, name_len);
|
2019-08-25 19:10:35 +00:00
|
|
|
|
2019-09-08 16:48:05 +00:00
|
|
|
int rc = rfb_pixfmt_from_fourcc(&msg->pixel_format, display->pixfmt);
|
|
|
|
if (rc < 0) {
|
2019-09-18 21:28:48 +00:00
|
|
|
client_unref(client);
|
2019-09-08 16:48:05 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2019-08-25 19:10:35 +00:00
|
|
|
msg->pixel_format.red_max = htons(msg->pixel_format.red_max);
|
|
|
|
msg->pixel_format.green_max = htons(msg->pixel_format.green_max);
|
|
|
|
msg->pixel_format.blue_max = htons(msg->pixel_format.blue_max);
|
2019-08-12 20:49:23 +00:00
|
|
|
|
2019-08-12 22:11:41 +00:00
|
|
|
vnc__write((uv_stream_t*)&client->stream_handle, msg, size, NULL);
|
2019-08-12 20:49:23 +00:00
|
|
|
|
|
|
|
free(msg);
|
|
|
|
}
|
|
|
|
|
2019-08-31 23:16:55 +00:00
|
|
|
static int on_init_message(struct nvnc_client *client)
|
2019-08-12 20:49:23 +00:00
|
|
|
{
|
|
|
|
if (client->buffer_len - client->buffer_index < 1)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
uint8_t shared_flag = client->msg_buffer[client->buffer_index];
|
2019-09-06 20:07:09 +00:00
|
|
|
if (!shared_flag)
|
2019-08-12 20:49:23 +00:00
|
|
|
disconnect_all_other_clients(client);
|
|
|
|
|
|
|
|
send_server_init_message(client);
|
|
|
|
|
2019-08-31 23:30:08 +00:00
|
|
|
nvnc_client_fn fn = client->server->new_client_fn;
|
|
|
|
if (fn)
|
|
|
|
fn(client);
|
|
|
|
|
2019-08-12 20:49:23 +00:00
|
|
|
client->state = VNC_CLIENT_STATE_READY;
|
|
|
|
return sizeof(shared_flag);
|
|
|
|
}
|
|
|
|
|
2019-08-31 23:16:55 +00:00
|
|
|
static int on_client_set_pixel_format(struct nvnc_client *client)
|
2019-08-12 20:49:23 +00:00
|
|
|
{
|
|
|
|
if (client->buffer_len - client->buffer_index
|
|
|
|
< 4 + sizeof(struct rfb_pixel_format))
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
struct rfb_pixel_format *fmt =
|
|
|
|
(struct rfb_pixel_format*)(client->msg_buffer +
|
|
|
|
client->buffer_index + 4);
|
|
|
|
|
|
|
|
if (!fmt->true_colour_flag) {
|
|
|
|
/* We don't really know what to do with color maps right now */
|
2019-09-18 21:28:48 +00:00
|
|
|
client_unref(client);
|
2019-08-12 20:49:23 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2019-08-25 19:10:35 +00:00
|
|
|
fmt->red_max = ntohs(fmt->red_max);
|
|
|
|
fmt->green_max = ntohs(fmt->green_max);
|
|
|
|
fmt->blue_max = ntohs(fmt->blue_max);
|
|
|
|
|
|
|
|
memcpy(&client->pixfmt, fmt, sizeof(client->pixfmt));
|
2019-08-12 20:49:23 +00:00
|
|
|
|
2019-08-25 19:10:35 +00:00
|
|
|
client->fourcc = rfb_pixfmt_to_fourcc(fmt);
|
|
|
|
|
2019-08-12 20:49:23 +00:00
|
|
|
return 4 + sizeof(struct rfb_pixel_format);
|
|
|
|
}
|
|
|
|
|
2019-08-31 23:16:55 +00:00
|
|
|
static int on_client_set_encodings(struct nvnc_client *client)
|
2019-08-12 23:33:06 +00:00
|
|
|
{
|
|
|
|
struct rfb_client_set_encodings_msg *msg =
|
|
|
|
(struct rfb_client_set_encodings_msg*)(client->msg_buffer +
|
|
|
|
client->buffer_index);
|
|
|
|
|
2019-09-19 18:14:24 +00:00
|
|
|
int n_encodings = MIN(MAX_ENCODINGS, ntohs(msg->n_encodings));
|
|
|
|
int n = 0;
|
2019-08-12 23:33:06 +00:00
|
|
|
|
|
|
|
for (int i = 0; i < n_encodings; ++i)
|
|
|
|
switch (msg->encodings[i]) {
|
2019-09-19 18:14:24 +00:00
|
|
|
case RFB_ENCODING_RAW:
|
|
|
|
case RFB_ENCODING_COPYRECT:
|
|
|
|
case RFB_ENCODING_RRE:
|
|
|
|
case RFB_ENCODING_HEXTILE:
|
|
|
|
case RFB_ENCODING_TRLE:
|
|
|
|
case RFB_ENCODING_ZRLE:
|
|
|
|
case RFB_ENCODING_CURSOR:
|
|
|
|
case RFB_ENCODING_DESKTOPSIZE:
|
|
|
|
client->encodings[n++] = msg->encodings[i];
|
2019-08-12 23:33:06 +00:00
|
|
|
}
|
|
|
|
|
2019-09-19 18:14:24 +00:00
|
|
|
client->n_encodings = n;
|
2019-08-12 23:33:06 +00:00
|
|
|
|
|
|
|
return sizeof(*msg) + 4 * n_encodings;
|
|
|
|
}
|
|
|
|
|
2019-08-31 23:16:55 +00:00
|
|
|
static int on_client_fb_update_request(struct nvnc_client *client)
|
2019-08-12 23:33:06 +00:00
|
|
|
{
|
2019-08-28 22:46:47 +00:00
|
|
|
struct nvnc *server = client->server;
|
|
|
|
|
2019-08-12 23:33:06 +00:00
|
|
|
struct rfb_client_fb_update_req_msg *msg =
|
|
|
|
(struct rfb_client_fb_update_req_msg*)(client->msg_buffer +
|
|
|
|
client->buffer_index);
|
|
|
|
|
|
|
|
int incremental = msg->incremental;
|
|
|
|
int x = ntohs(msg->x);
|
|
|
|
int y = ntohs(msg->y);
|
|
|
|
int width = ntohs(msg->width);
|
|
|
|
int height = ntohs(msg->height);
|
|
|
|
|
2019-08-28 22:46:47 +00:00
|
|
|
pixman_region_union_rect(&client->requested_region,
|
|
|
|
&client->requested_region,
|
|
|
|
x, y, width, height);
|
2019-08-12 23:33:06 +00:00
|
|
|
|
2019-08-28 22:46:47 +00:00
|
|
|
nvnc_fb_req_fn fn = server->fb_req_fn;
|
|
|
|
if (fn)
|
2019-08-31 23:16:55 +00:00
|
|
|
fn(client, incremental, x, y, width, height);
|
2019-08-27 21:49:28 +00:00
|
|
|
|
2019-08-12 23:33:06 +00:00
|
|
|
return sizeof(*msg);
|
|
|
|
}
|
|
|
|
|
2019-08-31 23:16:55 +00:00
|
|
|
static int on_client_key_event(struct nvnc_client *client)
|
2019-08-12 23:33:06 +00:00
|
|
|
{
|
2019-08-28 22:46:47 +00:00
|
|
|
struct nvnc *server = client->server;
|
|
|
|
|
2019-08-12 23:33:06 +00:00
|
|
|
struct rfb_client_key_event_msg *msg =
|
|
|
|
(struct rfb_client_key_event_msg*)(client->msg_buffer +
|
|
|
|
client->buffer_index);
|
|
|
|
|
|
|
|
int down_flag = msg->down_flag;
|
2019-08-28 22:46:47 +00:00
|
|
|
uint32_t keysym = ntohl(msg->key);
|
2019-08-12 23:33:06 +00:00
|
|
|
|
2019-08-28 22:46:47 +00:00
|
|
|
nvnc_key_fn fn = server->key_fn;
|
|
|
|
if (fn)
|
2019-08-31 23:16:55 +00:00
|
|
|
fn(client, keysym, !!down_flag);
|
2019-08-12 23:33:06 +00:00
|
|
|
|
|
|
|
return sizeof(*msg);
|
|
|
|
}
|
|
|
|
|
2019-08-31 23:16:55 +00:00
|
|
|
static int on_client_pointer_event(struct nvnc_client *client)
|
2019-08-12 23:33:06 +00:00
|
|
|
{
|
2019-08-28 22:46:47 +00:00
|
|
|
struct nvnc *server = client->server;
|
|
|
|
|
2019-08-12 23:33:06 +00:00
|
|
|
struct rfb_client_pointer_event_msg *msg =
|
|
|
|
(struct rfb_client_pointer_event_msg*)(client->msg_buffer +
|
|
|
|
client->buffer_index);
|
|
|
|
|
|
|
|
int button_mask = msg->button_mask;
|
|
|
|
uint16_t x = ntohs(msg->x);
|
|
|
|
uint16_t y = ntohs(msg->y);
|
|
|
|
|
2019-08-28 22:46:47 +00:00
|
|
|
nvnc_pointer_fn fn = server->pointer_fn;
|
|
|
|
if (fn)
|
2019-08-31 23:16:55 +00:00
|
|
|
fn(client, x, y, button_mask);
|
2019-08-12 23:33:06 +00:00
|
|
|
|
|
|
|
return sizeof(*msg);
|
|
|
|
}
|
|
|
|
|
2019-08-31 23:16:55 +00:00
|
|
|
static int on_client_cut_text(struct nvnc_client *client)
|
2019-08-12 23:39:37 +00:00
|
|
|
{
|
|
|
|
struct rfb_client_cut_text_msg *msg =
|
|
|
|
(struct rfb_client_cut_text_msg*)(client->msg_buffer +
|
|
|
|
client->buffer_index);
|
|
|
|
|
|
|
|
uint32_t length = ntohl(msg->length);
|
|
|
|
|
|
|
|
// TODO
|
|
|
|
|
|
|
|
return sizeof(*msg) + length;
|
|
|
|
}
|
|
|
|
|
2019-08-31 23:16:55 +00:00
|
|
|
static int on_client_message(struct nvnc_client *client)
|
2019-08-12 20:49:23 +00:00
|
|
|
{
|
|
|
|
if (client->buffer_len - client->buffer_index < 1)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
enum rfb_client_to_server_msg_type type =
|
|
|
|
client->msg_buffer[client->buffer_index];
|
|
|
|
|
|
|
|
switch (type) {
|
|
|
|
case RFB_CLIENT_TO_SERVER_SET_PIXEL_FORMAT:
|
2019-08-12 22:11:41 +00:00
|
|
|
return on_client_set_pixel_format(client);
|
2019-08-12 20:49:23 +00:00
|
|
|
case RFB_CLIENT_TO_SERVER_SET_ENCODINGS:
|
2019-08-12 23:33:06 +00:00
|
|
|
return on_client_set_encodings(client);
|
2019-08-12 20:49:23 +00:00
|
|
|
case RFB_CLIENT_TO_SERVER_FRAMEBUFFER_UPDATE_REQUEST:
|
2019-08-12 23:33:06 +00:00
|
|
|
return on_client_fb_update_request(client);
|
2019-08-12 20:49:23 +00:00
|
|
|
case RFB_CLIENT_TO_SERVER_KEY_EVENT:
|
2019-08-12 23:33:06 +00:00
|
|
|
return on_client_key_event(client);
|
2019-08-12 20:49:23 +00:00
|
|
|
case RFB_CLIENT_TO_SERVER_POINTER_EVENT:
|
2019-08-12 23:33:06 +00:00
|
|
|
return on_client_pointer_event(client);
|
2019-08-12 20:49:23 +00:00
|
|
|
case RFB_CLIENT_TO_SERVER_CLIENT_CUT_TEXT:
|
2019-08-12 23:39:37 +00:00
|
|
|
return on_client_cut_text(client);
|
2019-08-12 20:49:23 +00:00
|
|
|
}
|
|
|
|
|
2019-09-18 21:28:48 +00:00
|
|
|
client_unref(client);
|
2019-08-12 20:49:23 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2019-08-31 23:16:55 +00:00
|
|
|
static int try_read_client_message(struct nvnc_client *client)
|
2019-08-12 20:49:23 +00:00
|
|
|
{
|
|
|
|
switch (client->state) {
|
|
|
|
case VNC_CLIENT_STATE_ERROR:
|
2019-09-18 21:28:48 +00:00
|
|
|
client_unref(client);
|
2019-08-12 20:49:23 +00:00
|
|
|
return 0;
|
|
|
|
case VNC_CLIENT_STATE_WAITING_FOR_VERSION:
|
|
|
|
return on_version_message(client);
|
|
|
|
case VNC_CLIENT_STATE_WAITING_FOR_SECURITY:
|
|
|
|
return on_security_message(client);
|
|
|
|
case VNC_CLIENT_STATE_WAITING_FOR_INIT:
|
|
|
|
return on_init_message(client);
|
|
|
|
case VNC_CLIENT_STATE_READY:
|
|
|
|
return on_client_message(client);
|
|
|
|
}
|
|
|
|
|
|
|
|
abort();
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void on_client_read(uv_stream_t *stream, ssize_t n_read,
|
|
|
|
const uv_buf_t *buf)
|
|
|
|
{
|
2019-09-18 21:28:48 +00:00
|
|
|
struct nvnc_client *client =
|
|
|
|
container_of((uv_tcp_t*)stream, struct nvnc_client,
|
|
|
|
stream_handle);
|
|
|
|
|
2019-08-12 20:49:23 +00:00
|
|
|
if (n_read == UV_EOF) {
|
2019-09-18 21:28:48 +00:00
|
|
|
client_unref(client);
|
2019-08-12 20:49:23 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2019-08-12 22:11:41 +00:00
|
|
|
if (n_read < 0)
|
|
|
|
return;
|
|
|
|
|
2019-08-12 20:49:23 +00:00
|
|
|
assert(client->buffer_index == 0);
|
|
|
|
|
|
|
|
if (n_read > MSG_BUFFER_SIZE - client->buffer_len) {
|
|
|
|
/* Can't handle this. Let's just give up */
|
|
|
|
client->state = VNC_CLIENT_STATE_ERROR;
|
2019-09-18 21:28:48 +00:00
|
|
|
client_unref(client);
|
2019-08-12 20:49:23 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
memcpy(client->msg_buffer + client->buffer_len, buf->base, n_read);
|
|
|
|
client->buffer_len += n_read;
|
|
|
|
|
|
|
|
while (1) {
|
|
|
|
int rc = try_read_client_message(client);
|
|
|
|
if (rc == 0)
|
|
|
|
break;
|
|
|
|
|
|
|
|
client->buffer_index += rc;
|
|
|
|
}
|
|
|
|
|
2019-08-12 22:11:41 +00:00
|
|
|
assert(client->buffer_index <= client->buffer_len);
|
2019-08-12 20:49:23 +00:00
|
|
|
|
|
|
|
memmove(client->msg_buffer, client->msg_buffer + client->buffer_index,
|
|
|
|
client->buffer_index);
|
|
|
|
client->buffer_len -= client->buffer_index;
|
|
|
|
client->buffer_index = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void on_connection(uv_stream_t *server_stream, int status)
|
|
|
|
{
|
2019-08-31 23:16:55 +00:00
|
|
|
struct nvnc *server = container_of((uv_tcp_t*)server_stream,
|
|
|
|
struct nvnc, tcp_handle);
|
2019-08-12 20:49:23 +00:00
|
|
|
|
2019-08-31 23:16:55 +00:00
|
|
|
struct nvnc_client *client = calloc(1, sizeof(*client));
|
2019-08-12 20:49:23 +00:00
|
|
|
if (!client)
|
|
|
|
return;
|
|
|
|
|
2019-09-18 21:28:48 +00:00
|
|
|
client->ref = 1;
|
2019-08-12 20:49:23 +00:00
|
|
|
client->server = server;
|
|
|
|
|
2019-09-01 18:40:11 +00:00
|
|
|
if (deflateInit(&client->z_stream, Z_DEFAULT_COMPRESSION) != Z_OK) {
|
|
|
|
free(client);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2019-08-28 22:46:47 +00:00
|
|
|
pixman_region_init(&client->requested_region);
|
|
|
|
|
2019-08-12 22:11:41 +00:00
|
|
|
uv_tcp_init(uv_default_loop(), &client->stream_handle);
|
|
|
|
|
|
|
|
uv_accept((uv_stream_t*)&server->tcp_handle,
|
|
|
|
(uv_stream_t*)&client->stream_handle);
|
2019-08-12 20:49:23 +00:00
|
|
|
|
|
|
|
uv_read_start((uv_stream_t*)&client->stream_handle,
|
|
|
|
allocate_read_buffer, on_client_read);
|
|
|
|
|
|
|
|
vnc__write((uv_stream_t*)&client->stream_handle, RFB_VERSION_MESSAGE,
|
|
|
|
strlen(RFB_VERSION_MESSAGE), NULL);
|
|
|
|
|
|
|
|
LIST_INSERT_HEAD(&server->clients, client, link);
|
|
|
|
|
|
|
|
client->state = VNC_CLIENT_STATE_WAITING_FOR_VERSION;
|
|
|
|
}
|
|
|
|
|
2019-08-28 22:46:47 +00:00
|
|
|
int vnc_server_init(struct nvnc *self, const char* address, int port)
|
2019-08-12 20:49:23 +00:00
|
|
|
{
|
|
|
|
LIST_INIT(&self->clients);
|
|
|
|
|
|
|
|
uv_tcp_init(uv_default_loop(), &self->tcp_handle);
|
|
|
|
|
|
|
|
struct sockaddr_in addr = { 0 };
|
|
|
|
addr.sin_family = AF_INET;
|
|
|
|
addr.sin_addr.s_addr = inet_addr(address);
|
|
|
|
addr.sin_port = htons(port);
|
|
|
|
|
|
|
|
if (uv_tcp_bind(&self->tcp_handle, (const struct sockaddr*)&addr, 0) < 0)
|
|
|
|
goto failure;
|
|
|
|
|
|
|
|
if (uv_listen((uv_stream_t*)&self->tcp_handle, 16, on_connection) < 0)
|
|
|
|
goto failure;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
failure:
|
|
|
|
uv_unref((uv_handle_t*)&self->tcp_handle);
|
|
|
|
return -1;
|
|
|
|
}
|
2019-08-12 22:11:41 +00:00
|
|
|
|
2019-08-28 22:46:47 +00:00
|
|
|
EXPORT
|
|
|
|
struct nvnc *nvnc_open(const char *address, uint16_t port)
|
|
|
|
{
|
|
|
|
struct nvnc *self = calloc(1, sizeof(*self));
|
|
|
|
if (!self)
|
|
|
|
return NULL;
|
|
|
|
|
2019-08-29 21:47:02 +00:00
|
|
|
strcpy(self->display.name, DEFAULT_NAME);
|
|
|
|
|
2019-08-28 22:46:47 +00:00
|
|
|
LIST_INIT(&self->clients);
|
|
|
|
|
|
|
|
uv_tcp_init(uv_default_loop(), &self->tcp_handle);
|
|
|
|
|
|
|
|
struct sockaddr_in addr = { 0 };
|
|
|
|
addr.sin_family = AF_INET;
|
|
|
|
addr.sin_addr.s_addr = inet_addr(address);
|
|
|
|
addr.sin_port = htons(port);
|
|
|
|
|
|
|
|
if (uv_tcp_bind(&self->tcp_handle, (const struct sockaddr*)&addr, 0) < 0)
|
|
|
|
goto failure;
|
|
|
|
|
|
|
|
if (uv_listen((uv_stream_t*)&self->tcp_handle, 16, on_connection) < 0)
|
|
|
|
goto failure;
|
|
|
|
|
|
|
|
return self;
|
|
|
|
failure:
|
|
|
|
uv_unref((uv_handle_t*)&self->tcp_handle);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
EXPORT
|
|
|
|
void nvnc_close(struct nvnc *self)
|
|
|
|
{
|
2019-08-31 23:16:55 +00:00
|
|
|
struct nvnc_client *client;
|
2019-08-28 22:46:47 +00:00
|
|
|
|
|
|
|
LIST_FOREACH(client, &self->clients, link)
|
2019-09-18 21:28:48 +00:00
|
|
|
client_unref(client);
|
2019-08-28 22:46:47 +00:00
|
|
|
|
|
|
|
uv_unref((uv_handle_t*)&self->tcp_handle);
|
|
|
|
free(self);
|
|
|
|
}
|
|
|
|
|
2019-09-06 19:26:18 +00:00
|
|
|
static void free_write_buffer(uv_write_t *req, int status)
|
|
|
|
{
|
|
|
|
struct vnc_write_request *rq = (struct vnc_write_request*)req;
|
|
|
|
free(rq->buffer.base);
|
|
|
|
}
|
|
|
|
|
2019-09-19 18:14:24 +00:00
|
|
|
enum rfb_encodings choose_frame_encoding(struct nvnc_client *client)
|
|
|
|
{
|
|
|
|
for (int i = 0; i < client->n_encodings; ++i)
|
|
|
|
switch (client->encodings[i]) {
|
|
|
|
case RFB_ENCODING_RAW:
|
|
|
|
case RFB_ENCODING_ZRLE:
|
|
|
|
return client->encodings[i];
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2019-09-15 21:51:07 +00:00
|
|
|
void do_client_update_fb(uv_work_t *work)
|
|
|
|
{
|
|
|
|
struct fb_update_work *update = (void*)work;
|
|
|
|
struct nvnc_client *client = update->client;
|
|
|
|
const struct nvnc_fb *fb = update->fb;
|
|
|
|
|
2019-09-19 18:14:24 +00:00
|
|
|
enum rfb_encodings encoding = choose_frame_encoding(client);
|
|
|
|
assert(encoding != -1);
|
|
|
|
|
|
|
|
switch (encoding) {
|
|
|
|
case RFB_ENCODING_RAW:
|
|
|
|
raw_encode_frame(&update->frame, &client->pixfmt, fb,
|
|
|
|
&update->server_fmt, &update->region);
|
|
|
|
break;
|
|
|
|
case RFB_ENCODING_ZRLE:
|
|
|
|
zrle_encode_frame(&client->z_stream, &update->frame,
|
|
|
|
&client->pixfmt, fb, &update->server_fmt,
|
|
|
|
&update->region);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
2019-09-15 21:51:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void on_client_update_fb_done(uv_work_t *work, int status)
|
|
|
|
{
|
|
|
|
(void)status;
|
|
|
|
|
|
|
|
struct fb_update_work *update = (void*)work;
|
|
|
|
struct nvnc_client *client = update->client;
|
|
|
|
struct nvnc *server = client->server;
|
|
|
|
struct vec *frame = &update->frame;
|
|
|
|
|
2019-09-18 21:28:48 +00:00
|
|
|
if (!uv_is_closing((uv_handle_t*)&client->stream_handle))
|
|
|
|
vnc__write((uv_stream_t*)&client->stream_handle,
|
|
|
|
frame->data, frame->len, free_write_buffer);
|
2019-09-15 21:51:07 +00:00
|
|
|
|
|
|
|
if (--server->n_pending_updates == 0)
|
|
|
|
if (update->on_done)
|
|
|
|
update->on_done(server);
|
2019-09-18 21:21:37 +00:00
|
|
|
|
|
|
|
assert(server->n_pending_updates >= 0);
|
2019-09-18 21:28:48 +00:00
|
|
|
client_unref(client);
|
2019-09-15 21:51:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
int schedule_client_update_fb(struct nvnc_client *client,
|
|
|
|
const struct nvnc_fb *fb,
|
|
|
|
struct pixman_region16 *region,
|
|
|
|
nvnc_update_done_fn on_update_done)
|
|
|
|
{
|
|
|
|
struct fb_update_work *work = calloc(1, sizeof(*work));
|
|
|
|
if (!work)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
if (rfb_pixfmt_from_fourcc(&work->server_fmt, fb->fourcc_format) < 0)
|
|
|
|
goto pixfmt_failure;
|
|
|
|
|
|
|
|
work->client = client;
|
|
|
|
work->fb = fb;
|
|
|
|
work->on_done = on_update_done;
|
|
|
|
|
|
|
|
/* The client's region is exchanged for an empty one */
|
|
|
|
work->region = client->requested_region;
|
|
|
|
pixman_region_init(&client->requested_region);
|
|
|
|
|
|
|
|
int rc = vec_init(&work->frame, fb->width * fb->height * 3 / 2);
|
|
|
|
if (rc < 0)
|
|
|
|
goto vec_failure;
|
|
|
|
|
2019-09-18 21:28:48 +00:00
|
|
|
client_ref(client);
|
|
|
|
|
2019-09-15 21:51:07 +00:00
|
|
|
rc = uv_queue_work(uv_default_loop(), &work->work, do_client_update_fb,
|
|
|
|
on_client_update_fb_done);
|
|
|
|
if (rc < 0)
|
|
|
|
goto queue_failure;
|
|
|
|
|
|
|
|
client->server->n_pending_updates++;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
queue_failure:
|
2019-09-18 21:28:48 +00:00
|
|
|
client_unref(client);
|
2019-09-15 21:51:07 +00:00
|
|
|
vec_destroy(&work->frame);
|
|
|
|
vec_failure:
|
|
|
|
pixfmt_failure:
|
|
|
|
free(work);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2019-08-28 22:46:47 +00:00
|
|
|
EXPORT
|
|
|
|
int nvnc_update_fb(struct nvnc *self, const struct nvnc_fb *fb,
|
2019-09-15 21:51:07 +00:00
|
|
|
const struct pixman_region16 *input_region,
|
|
|
|
nvnc_update_done_fn on_update_done)
|
2019-08-28 22:46:47 +00:00
|
|
|
{
|
|
|
|
int rc = -1;
|
|
|
|
|
2019-08-29 14:02:29 +00:00
|
|
|
/* TODO: Support some more tiling modes */
|
|
|
|
if (fb->fourcc_modifier != DRM_FORMAT_MOD_LINEAR)
|
|
|
|
return -1;
|
|
|
|
|
2019-09-15 21:51:07 +00:00
|
|
|
/* Scheduling new updates would cause racing */
|
|
|
|
if (self->n_pending_updates > 0)
|
2019-09-08 16:48:05 +00:00
|
|
|
return -1;
|
2019-08-28 22:46:47 +00:00
|
|
|
|
|
|
|
struct pixman_region16 region;
|
|
|
|
pixman_region_init(®ion);
|
|
|
|
|
|
|
|
pixman_region_intersect_rect(®ion,
|
|
|
|
(struct pixman_region16*)input_region,
|
|
|
|
0, 0, fb->width, fb->height);
|
|
|
|
|
2019-08-31 23:16:55 +00:00
|
|
|
struct nvnc_client *client;
|
2019-08-28 22:46:47 +00:00
|
|
|
|
|
|
|
LIST_FOREACH(client, &self->clients, link) {
|
2019-09-08 16:46:13 +00:00
|
|
|
if (uv_is_closing((uv_handle_t*)&client->stream_handle))
|
2019-09-06 20:07:09 +00:00
|
|
|
continue;
|
|
|
|
|
2019-08-28 22:46:47 +00:00
|
|
|
struct pixman_region16* cregion = &client->requested_region;
|
|
|
|
|
|
|
|
pixman_region_intersect(cregion, cregion, ®ion);
|
|
|
|
|
2019-09-06 20:17:03 +00:00
|
|
|
if (!pixman_region_not_empty(cregion))
|
|
|
|
continue;
|
|
|
|
|
2019-09-15 21:51:07 +00:00
|
|
|
schedule_client_update_fb(client, fb, ®ion, on_update_done);
|
2019-08-28 22:46:47 +00:00
|
|
|
}
|
|
|
|
|
2019-09-18 21:21:37 +00:00
|
|
|
rc = self->n_pending_updates > 0 ? 0 : -1;
|
2019-08-28 22:46:47 +00:00
|
|
|
failure:
|
|
|
|
pixman_region_fini(®ion);
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
|
|
|
EXPORT
|
2019-08-31 23:16:55 +00:00
|
|
|
void nvnc_set_userdata(void *self, void *userdata)
|
2019-08-28 22:46:47 +00:00
|
|
|
{
|
2019-08-31 23:16:55 +00:00
|
|
|
struct nvnc_common *common = self;
|
|
|
|
common->userdata = userdata;
|
2019-08-28 22:46:47 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
EXPORT
|
2019-08-31 23:16:55 +00:00
|
|
|
void* nvnc_get_userdata(const void *self)
|
2019-08-28 22:46:47 +00:00
|
|
|
{
|
2019-08-31 23:16:55 +00:00
|
|
|
const struct nvnc_common *common = self;
|
|
|
|
return common->userdata;
|
2019-08-28 22:46:47 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
EXPORT
|
|
|
|
void nvnc_set_key_fn(struct nvnc *self, nvnc_key_fn fn)
|
|
|
|
{
|
|
|
|
self->key_fn = fn;
|
|
|
|
}
|
|
|
|
|
|
|
|
EXPORT
|
|
|
|
void nvnc_set_pointer_fn(struct nvnc *self, nvnc_pointer_fn fn)
|
|
|
|
{
|
|
|
|
self->pointer_fn = fn;
|
|
|
|
}
|
|
|
|
|
|
|
|
EXPORT
|
|
|
|
void nvnc_set_fb_req_fn(struct nvnc *self, nvnc_fb_req_fn fn)
|
|
|
|
{
|
|
|
|
self->fb_req_fn = fn;
|
|
|
|
}
|
|
|
|
|
2019-08-31 23:30:08 +00:00
|
|
|
EXPORT
|
|
|
|
void nvnc_set_new_client_fn(struct nvnc *self, nvnc_client_fn fn)
|
|
|
|
{
|
|
|
|
self->new_client_fn = fn;
|
|
|
|
}
|
|
|
|
|
|
|
|
EXPORT
|
|
|
|
void nvnc_set_client_cleanup_fn(struct nvnc_client *self, nvnc_client_fn fn)
|
|
|
|
{
|
|
|
|
self->cleanup_fn = fn;
|
|
|
|
}
|
|
|
|
|
2019-08-29 21:47:02 +00:00
|
|
|
EXPORT
|
|
|
|
void nvnc_set_dimensions(struct nvnc *self, uint16_t width, uint16_t height,
|
|
|
|
uint32_t fourcc_format)
|
2019-08-12 22:11:41 +00:00
|
|
|
{
|
2019-08-29 21:47:02 +00:00
|
|
|
self->display.width = width;
|
|
|
|
self->display.height = height;
|
|
|
|
self->display.pixfmt = fourcc_format;
|
|
|
|
}
|
2019-08-12 22:11:41 +00:00
|
|
|
|
2019-08-31 23:16:55 +00:00
|
|
|
EXPORT
|
|
|
|
struct nvnc *nvnc_get_server(const struct nvnc_client *client)
|
|
|
|
{
|
|
|
|
return client->server;
|
|
|
|
}
|
|
|
|
|
2019-08-29 21:47:02 +00:00
|
|
|
EXPORT
|
|
|
|
void nvnc_set_name(struct nvnc *self, const char *name)
|
|
|
|
{
|
|
|
|
strncpy(self->display.name, name, sizeof(self->display.name));
|
|
|
|
self->display.name[sizeof(self->display.name) - 1] = '\0';
|
2019-08-12 22:11:41 +00:00
|
|
|
}
|