diff --git a/Makefile b/Makefile index d2ed0c1..c65a4a0 100644 --- a/Makefile +++ b/Makefile @@ -5,6 +5,7 @@ SOURCES := \ src/util.c \ src/vec.c \ src/zrle.c \ + src/raw-encoding.c \ src/pixels.c \ src/damage.c \ diff --git a/include/raw-encoding.h b/include/raw-encoding.h new file mode 100644 index 0000000..6a8c4e8 --- /dev/null +++ b/include/raw-encoding.h @@ -0,0 +1,12 @@ +#pragma once + +struct nvnc_fb; +struct rfb_pixel_format; +struct pixman_region16; +struct vec; + +int raw_encode_frame(struct vec *dst, + const struct rfb_pixel_format *dst_fmt, + const struct nvnc_fb *src, + const struct rfb_pixel_format *src_fmt, + struct pixman_region16 *region); diff --git a/include/vec.h b/include/vec.h index 40e18fc..1b0bc2b 100644 --- a/include/vec.h +++ b/include/vec.h @@ -52,7 +52,8 @@ static inline void vec_fast_append_32(struct vec* vec, uint32_t value) { assert(vec->len + sizeof(value) <= vec->cap); assert(vec->len % sizeof(value) == 0); - ((uint32_t*)vec->data)[vec->len] = value; + uint32_t* p = (uint32_t*)((uint8_t*)vec->data + vec->len); + *p = value; vec->len += sizeof(value); } diff --git a/src/raw-encoding.c b/src/raw-encoding.c new file mode 100644 index 0000000..a4ea18a --- /dev/null +++ b/src/raw-encoding.c @@ -0,0 +1,80 @@ +#include "neatvnc.h" +#include "rfb-proto.h" +#include "vec.h" + +#include + +int raw_encode_box(struct vec *dst, const struct rfb_pixel_format *dst_fmt, + const struct nvnc_fb *fb, + const struct rfb_pixel_format *src_fmt, + int x_start, int y_start, int stride, int width, int height) +{ + int rc = -1; + + struct rfb_server_fb_rect rect = { + .encoding = htonl(RFB_ENCODING_RAW), + .x = htons(x_start), + .y = htons(y_start), + .width = htons(width), + .height = htons(height), + }; + + rc = vec_reserve(dst, width * height * 4 + 256); + if (rc < 0) + return -1; + + rc = vec_append(dst, &rect, sizeof(rect)); + if (rc < 0) + return -1; + + uint32_t* b = fb->addr; + + if (fb->nvnc_modifier & NVNC_MOD_Y_INVERT) + y_start = fb->height - y_start - height; + + /* TODO: Pixel format conversion */ + for (int y = y_start; y < y_start + height; ++y) + for (int x = x_start; x < x_start + width; ++x) + vec_fast_append_32(dst, b[x + y * stride]); + + return 0; +} + +int raw_encode_frame(struct vec *dst, + const struct rfb_pixel_format *dst_fmt, + const struct nvnc_fb *src, + const struct rfb_pixel_format *src_fmt, + struct pixman_region16 *region) +{ + int rc = -1; + + int n_rects = 0; + struct pixman_box16 *box = pixman_region_rectangles(region, &n_rects); + if (n_rects > UINT16_MAX) { + box = pixman_region_extents(region); + n_rects = 1; + } + + struct rfb_server_fb_update_msg head = { + .type = RFB_SERVER_TO_CLIENT_FRAMEBUFFER_UPDATE, + .n_rects = htons(n_rects), + }; + + rc = vec_append(dst, &head, sizeof(head)); + if (rc < 0) + return -1; + + for (int i = 0; i < n_rects; ++i) { + int x = box[i].x1; + int y = box[i].y1; + int box_width = box[i].x2 - x; + int box_height = box[i].y2 - y; + + rc = raw_encode_box(dst, dst_fmt, src, src_fmt, x, y, + src->width, box_width, box_height); + if (rc < 0) + return -1; + } + + return 0; +} diff --git a/src/server.c b/src/server.c index b55b463..ac77569 100644 --- a/src/server.c +++ b/src/server.c @@ -17,6 +17,7 @@ #include "rfb-proto.h" #include "util.h" #include "zrle.h" +#include "raw-encoding.h" #include "vec.h" #include "type-macros.h" #include "neatvnc.h" @@ -42,18 +43,9 @@ #define READ_BUFFER_SIZE 4096 #define MSG_BUFFER_SIZE 4096 -#define EXPORT __attribute__((visibility("default"))) +#define MAX_ENCODINGS 32 -enum vnc_encodings { - VNC_ENCODING_RAW = 1 << 0, - VNC_ENCODING_COPYRECT = 1 << 1, - VNC_ENCODING_RRE = 1 << 2, - VNC_ENCODING_HEXTILE = 1 << 3, - VNC_ENCODING_TRLE = 1 << 4, - VNC_ENCODING_ZRLE = 1 << 5, - VNC_ENCODING_CURSOR = 1 << 6, - VNC_ENCODING_DESKTOPSIZE = 1 << 7, -}; +#define EXPORT __attribute__((visibility("default"))) enum nvnc_client_state { VNC_CLIENT_STATE_ERROR = -1, @@ -77,7 +69,8 @@ struct nvnc_client { enum nvnc_client_state state; uint32_t fourcc; struct rfb_pixel_format pixfmt; - enum vnc_encodings encodings; + enum rfb_encodings encodings[MAX_ENCODINGS + 1]; + size_t n_encodings; LIST_ENTRY(nvnc_client) link; struct pixman_region16 requested_region; nvnc_client_fn cleanup_fn; @@ -524,23 +517,23 @@ static int on_client_set_encodings(struct nvnc_client *client) (struct rfb_client_set_encodings_msg*)(client->msg_buffer + client->buffer_index); - int n_encodings = ntohs(msg->n_encodings); - - uint32_t e = 0; + int n_encodings = MIN(MAX_ENCODINGS, ntohs(msg->n_encodings)); + int n = 0; for (int i = 0; i < n_encodings; ++i) switch (msg->encodings[i]) { - case RFB_ENCODING_RAW: e |= VNC_ENCODING_RAW; - case RFB_ENCODING_COPYRECT: e |= VNC_ENCODING_COPYRECT; - case RFB_ENCODING_RRE: e |= VNC_ENCODING_RRE; - case RFB_ENCODING_HEXTILE: e |= VNC_ENCODING_HEXTILE; - case RFB_ENCODING_TRLE: e |= VNC_ENCODING_TRLE; - case RFB_ENCODING_ZRLE: e |= VNC_ENCODING_ZRLE; - case RFB_ENCODING_CURSOR: e |= VNC_ENCODING_CURSOR; - case RFB_ENCODING_DESKTOPSIZE: e |= VNC_ENCODING_COPYRECT; + 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]; } - client->encodings = e; + client->n_encodings = n; return sizeof(*msg) + 4 * n_encodings; } @@ -817,14 +810,42 @@ static void free_write_buffer(uv_write_t *req, int status) free(rq->buffer.base); } +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; +} + 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; - zrle_encode_frame(&client->z_stream, &update->frame, &client->pixfmt, - fb, &update->server_fmt, &update->region); + 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; + } } void on_client_update_fb_done(uv_work_t *work, int status)