diff --git a/examples/draw.c b/examples/draw.c index 0184a20..efb6a3b 100644 --- a/examples/draw.c +++ b/examples/draw.c @@ -43,7 +43,7 @@ void on_fb_req(struct nvnc_client *client, bool incremental, uint16_t x, uint16_ struct pixman_region16 region; pixman_region_init_rect(®ion, 0, 0, draw->fb.width, draw->fb.height); - nvnc_update_fb(server, &draw->fb, ®ion); + nvnc_update_fb(server, &draw->fb, ®ion, NULL); pixman_region_fini(®ion); } @@ -67,7 +67,7 @@ void on_pointer_event(struct nvnc_client *client, uint16_t x, uint16_t y, pixman_region_init_rect(®ion, 0, 0, draw->fb.width, draw->fb.height); pixman_region_intersect_rect(®ion, ®ion, x, y, 1, 1); - nvnc_update_fb(server, &draw->fb, ®ion); + nvnc_update_fb(server, &draw->fb, ®ion, NULL); pixman_region_fini(®ion); } diff --git a/examples/png-server.c b/examples/png-server.c index 043fd99..86a654c 100644 --- a/examples/png-server.c +++ b/examples/png-server.c @@ -37,7 +37,7 @@ void on_fb_req(struct nvnc_client *client, bool incremental, struct pixman_region16 region; pixman_region_init_rect(®ion, 0, 0, fb->width, fb->height); - nvnc_update_fb(server, fb, ®ion); + nvnc_update_fb(server, fb, ®ion, NULL); pixman_region_fini(®ion); } diff --git a/include/neatvnc.h b/include/neatvnc.h index 3919aad..25a8071 100644 --- a/include/neatvnc.h +++ b/include/neatvnc.h @@ -54,6 +54,7 @@ typedef void (*nvnc_fb_req_fn)(struct nvnc_client*, bool is_incremental, uint16_t width, uint16_t height); typedef void (*nvnc_client_fn)(struct nvnc_client*); typedef void (*nvnc_damage_fn)(struct pixman_region16 *damage, void *userdata); +typedef void (*nvnc_update_done_fn)(struct nvnc*); struct nvnc *nvnc_open(const char *addr, uint16_t port); void nvnc_close(struct nvnc *self); @@ -80,7 +81,8 @@ void nvnc_set_client_cleanup_fn(struct nvnc_client *self, nvnc_client_fn fn); * Only the region specified by the region argument is updated. */ int nvnc_update_fb(struct nvnc *self, const struct nvnc_fb* fb, - const struct pixman_region16* region); + const struct pixman_region16* region, + nvnc_update_done_fn on_update_done); /* * Find the regions that differ between fb0 and fb1. Regions outside the hinted diff --git a/src/server.c b/src/server.c index 22ad76b..1435f02 100644 --- a/src/server.c +++ b/src/server.c @@ -28,6 +28,7 @@ #include #include #include +#include #ifndef DRM_FORMAT_INVALID #define DRM_FORMAT_INVALID 0 @@ -100,12 +101,23 @@ struct nvnc { struct nvnc_client_list clients; struct vnc_display display; void *userdata; + int n_pending_updates; nvnc_key_fn key_fn; nvnc_pointer_fn pointer_fn; nvnc_fb_req_fn fb_req_fn; nvnc_client_fn new_client_fn; }; +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; +}; + static const char* fourcc_to_string(uint32_t fourcc) { static char buffer[5]; @@ -784,9 +796,78 @@ static void free_write_buffer(uv_write_t *req, int status) free(rq->buffer.base); } +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); +} + +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; + + vnc__write((uv_stream_t*)&client->stream_handle, + frame->data, frame->len, free_write_buffer); + + if (--server->n_pending_updates == 0) + if (update->on_done) + update->on_done(server); +} + +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; + + 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: + vec_destroy(&work->frame); +vec_failure: +pixfmt_failure: + free(work); + return -1; +} + EXPORT int nvnc_update_fb(struct nvnc *self, const struct nvnc_fb *fb, - const struct pixman_region16 *input_region) + const struct pixman_region16 *input_region, + nvnc_update_done_fn on_update_done) { int rc = -1; @@ -794,8 +875,8 @@ int nvnc_update_fb(struct nvnc *self, const struct nvnc_fb *fb, if (fb->fourcc_modifier != DRM_FORMAT_MOD_LINEAR) return -1; - struct rfb_pixel_format server_fmt; - if (rfb_pixfmt_from_fourcc(&server_fmt, fb->fourcc_format) < 0) + /* Scheduling new updates would cause racing */ + if (self->n_pending_updates > 0) return -1; struct pixman_region16 region; @@ -818,18 +899,7 @@ int nvnc_update_fb(struct nvnc *self, const struct nvnc_fb *fb, if (!pixman_region_not_empty(cregion)) continue; - struct vec frame; - rc = vec_init(&frame, fb->width * fb->height * 3 / 2); - if (rc < 0) - goto failure; - - zrle_encode_frame(&client->z_stream, &frame, &client->pixfmt, - fb, &server_fmt, cregion); - - pixman_region_clear(cregion); - - vnc__write((uv_stream_t*)&client->stream_handle, frame.data, - frame.len, free_write_buffer); + schedule_client_update_fb(client, fb, ®ion, on_update_done); } rc = 0;