diff --git a/examples/draw.c b/examples/draw.c index 4a052c6..fa866ae 100644 --- a/examples/draw.c +++ b/examples/draw.c @@ -25,8 +25,12 @@ #include #include +#define MAX_COORD 128 + struct draw { struct nvnc_fb* fb; + struct { uint16_t x, y; } coord[MAX_COORD]; + int index; }; void on_pointer_event(struct nvnc_client* client, uint16_t x, uint16_t y, @@ -41,19 +45,41 @@ void on_pointer_event(struct nvnc_client* client, uint16_t x, uint16_t y, struct draw* draw = nvnc_get_userdata(server); assert(draw); - uint32_t* image = nvnc_fb_get_addr(draw->fb); int width = nvnc_fb_get_width(draw->fb); int height = nvnc_fb_get_height(draw->fb); - image[x + y * width] = 0; + if (draw->index >= MAX_COORD) + return; + + draw->coord[draw->index].x = x; + draw->coord[draw->index].y = y; + draw->index++; struct pixman_region16 region; pixman_region_init_rect(®ion, 0, 0, width, height); pixman_region_intersect_rect(®ion, ®ion, x, y, 1, 1); - nvnc_feed_frame(server, draw->fb, ®ion); + nvnc_damage_region(server, ®ion); pixman_region_fini(®ion); } +void on_render(struct nvnc* server, struct nvnc_fb* fb) +{ + struct draw* draw = nvnc_get_userdata(server); + assert(draw); + + uint32_t* image = nvnc_fb_get_addr(draw->fb); + int width = nvnc_fb_get_width(draw->fb); + + for (int i = 0; i < draw->index; ++i) { + uint16_t x = draw->coord[i].x; + uint16_t y = draw->coord[i].y; + + image[x + y * width] = 0; + } + + draw->index = 0; +} + void on_sigint() { aml_exit(aml_get_default()); @@ -61,7 +87,7 @@ void on_sigint() int main(int argc, char* argv[]) { - struct draw draw; + struct draw draw = { 0 }; int width = 500, height = 500; uint32_t format = DRM_FORMAT_RGBX8888; @@ -81,6 +107,8 @@ int main(int argc, char* argv[]) nvnc_set_name(server, "Draw"); nvnc_set_pointer_fn(server, on_pointer_event); nvnc_set_userdata(server, &draw); + nvnc_set_buffer(server, draw.fb); + nvnc_set_render_fn(server, on_render); struct aml_signal* sig = aml_signal_new(SIGINT, on_sigint, NULL, NULL); aml_start(aml_get_default(), sig); diff --git a/examples/png-server.c b/examples/png-server.c index 6efaeb9..02d1242 100644 --- a/examples/png-server.c +++ b/examples/png-server.c @@ -54,13 +54,10 @@ int main(int argc, char* argv[]) uint32_t fourcc_format = nvnc_fb_get_fourcc_format(fb); nvnc_set_dimensions(server, width, height, fourcc_format); + nvnc_set_buffer(server, fb); nvnc_set_name(server, file); - struct pixman_region16 region; - pixman_region_init_rect(®ion, 0, 0, nvnc_fb_get_width(fb), - nvnc_fb_get_height(fb)); - nvnc_feed_frame(server, fb, ®ion); - pixman_region_fini(®ion); + nvnc_damage_whole(server); struct aml_signal* sig = aml_signal_new(SIGINT, on_sigint, NULL, NULL); aml_start(aml_get_default(), sig); diff --git a/include/common.h b/include/common.h index c9a95fb..cc2c23d 100644 --- a/include/common.h +++ b/include/common.h @@ -51,6 +51,7 @@ enum nvnc_client_state { struct nvnc; struct stream; struct aml_handler; +struct aml_idle; struct nvnc_common { void* userdata; @@ -70,7 +71,6 @@ struct nvnc_client { struct pixman_region16 damage; int n_pending_requests; bool is_updating; - bool needs_whole_frame; nvnc_client_fn cleanup_fn; z_stream z_stream; struct tight_encoder tight_encoder; @@ -92,6 +92,7 @@ struct nvnc { struct nvnc_common common; int fd; struct aml_handler* poll_handle; + struct aml_idle* dispatch_handler; struct nvnc_client_list clients; struct vnc_display display; void* userdata; @@ -99,7 +100,8 @@ struct nvnc { nvnc_pointer_fn pointer_fn; nvnc_fb_req_fn fb_req_fn; nvnc_client_fn new_client_fn; - struct nvnc_fb* frame; + nvnc_render_fn render_fn; + struct nvnc_fb* buffer; #ifdef ENABLE_TLS gnutls_certificate_credentials_t tls_creds; diff --git a/include/neatvnc.h b/include/neatvnc.h index 27fb75a..ffd3b72 100644 --- a/include/neatvnc.h +++ b/include/neatvnc.h @@ -47,6 +47,7 @@ typedef void (*nvnc_client_fn)(struct nvnc_client*); typedef void (*nvnc_damage_fn)(struct pixman_region16* damage, void* userdata); typedef bool (*nvnc_auth_fn)(const char* username, const char* password, void* userdata); +typedef void (*nvnc_render_fn)(struct nvnc*, struct nvnc_fb*); struct nvnc* nvnc_open(const char* addr, uint16_t port); void nvnc_close(struct nvnc* self); @@ -59,12 +60,15 @@ struct nvnc* nvnc_get_server(const struct nvnc_client* client); void nvnc_set_dimensions(struct nvnc* self, uint16_t width, uint16_t height, uint32_t fourcc_format); +void nvnc_set_buffer(struct nvnc*, struct nvnc_fb*); + void nvnc_set_name(struct nvnc* self, const char* name); void nvnc_set_key_fn(struct nvnc* self, nvnc_key_fn); void nvnc_set_pointer_fn(struct nvnc* self, nvnc_pointer_fn); void nvnc_set_fb_req_fn(struct nvnc* self, nvnc_fb_req_fn); void nvnc_set_new_client_fn(struct nvnc* self, nvnc_client_fn); +void nvnc_set_render_fn(struct nvnc* self, nvnc_render_fn fn); void nvnc_set_client_cleanup_fn(struct nvnc_client* self, nvnc_client_fn fn); bool nvnc_has_auth(void); @@ -77,9 +81,6 @@ struct nvnc_fb* nvnc_fb_new(uint16_t width, uint16_t height, void nvnc_fb_ref(struct nvnc_fb* fb); void nvnc_fb_unref(struct nvnc_fb* fb); -bool nvnc_fb_lock(struct nvnc_fb*); -void nvnc_fb_unlock(struct nvnc_fb*); - enum nvnc_fb_flags nvnc_fb_get_flags(const struct nvnc_fb*); void nvnc_fb_set_flags(struct nvnc_fb*, enum nvnc_fb_flags); @@ -88,12 +89,8 @@ uint16_t nvnc_fb_get_width(const struct nvnc_fb* fb); uint16_t nvnc_fb_get_height(const struct nvnc_fb* fb); uint32_t nvnc_fb_get_fourcc_format(const struct nvnc_fb* fb); -/* - * Feed a new frame to the server. The damaged region is sent to clients - * immediately. - */ -int nvnc_feed_frame(struct nvnc* self, struct nvnc_fb* fb, - const struct pixman_region16* damage); +void nvnc_damage_region(struct nvnc*, const struct pixman_region16* damage); +void nvnc_damage_whole(struct nvnc*); /* * Find the regions that differ between fb0 and fb1. Regions outside the hinted diff --git a/src/server.c b/src/server.c index 45e3d91..12c19b7 100644 --- a/src/server.c +++ b/src/server.c @@ -465,7 +465,7 @@ static int on_client_set_encodings(struct nvnc_client* client) static void process_fb_update_requests(struct nvnc_client* client) { - if (!client->server->frame) + if (!client->server->buffer) return; if (client->net_stream->state == STREAM_STATE_CLOSED) @@ -477,23 +477,9 @@ static void process_fb_update_requests(struct nvnc_client* client) if (client->is_updating || client->n_pending_requests == 0) return; - struct nvnc_fb* fb = client->server->frame; - - if (!nvnc_fb_lock(fb)) - return; - - if (client->needs_whole_frame && (fb->flags & NVNC_FB_PARTIAL)) - goto abort; - client->is_updating = true; if (schedule_client_update_fb(client) < 0) - goto abort; - - return; - -abort: - nvnc_fb_unlock(fb); - client->is_updating = false; + client->is_updating = false; } static int on_client_fb_update_request(struct nvnc_client* client) @@ -513,9 +499,6 @@ static int on_client_fb_update_request(struct nvnc_client* client) int width = ntohs(msg->width); int height = ntohs(msg->height); - if (!incremental) - client->needs_whole_frame = true; - client->n_pending_requests++; /* Note: The region sent from the client is ignored for incremental @@ -531,8 +514,6 @@ static int on_client_fb_update_request(struct nvnc_client* client) if (fn) fn(client, incremental, x, y, width, height); - process_fb_update_requests(client); - return sizeof(*msg); } @@ -812,6 +793,31 @@ failure: return fd; } +bool nvnc__is_damaged(struct nvnc* self) +{ + struct nvnc_client* client; + LIST_FOREACH(client, &self->clients, link) + if (pixman_region_not_empty(&client->damage)) + return true; + + return false; +} + +void on_main_dispatch(void* aml_obj) +{ + struct nvnc* self = aml_get_userdata(aml_obj); + + if (!nvnc__is_damaged(self)) + return; + + if (self->render_fn) + self->render_fn(self, self->buffer); + + struct nvnc_client* client; + LIST_FOREACH(client, &self->clients, link) + process_fb_update_requests(client); +} + EXPORT struct nvnc* nvnc_open(const char* address, uint16_t port) { @@ -837,11 +843,22 @@ struct nvnc* nvnc_open(const char* address, uint16_t port) goto failure; if (aml_start(aml_get_default(), self->poll_handle) < 0) - goto start_failure; + goto poll_start_failure; + + self->dispatch_handler = aml_idle_new(on_main_dispatch, self, NULL); + if (!self->dispatch_handler) + goto new_idle_failure; + + if (aml_start(aml_get_default(), self->dispatch_handler) < 0) + goto idle_start_failure; return self; -start_failure: +idle_start_failure: + aml_unref(self->dispatch_handler); +new_idle_failure: + aml_stop(aml_get_default(), self->poll_handle); +poll_start_failure: aml_unref(self->poll_handle); failure: close(self->fd); @@ -853,13 +870,14 @@ void nvnc_close(struct nvnc* self) { struct nvnc_client* client; - if (self->frame) - nvnc_fb_unref(self->frame); + if (self->buffer) + nvnc_fb_unref(self->buffer); struct nvnc_client* tmp; LIST_FOREACH_SAFE (client, &self->clients, link, tmp) client_unref(client); + aml_stop(aml_get_default(), self->dispatch_handler); aml_stop(aml_get_default(), self->poll_handle); close(self->fd); @@ -943,7 +961,6 @@ void on_client_update_fb_done(void* work) struct nvnc_client* client = update->client; struct vec* frame = &update->frame; - nvnc_fb_unlock(update->fb); nvnc_fb_unref(update->fb); client_ref(client); @@ -954,7 +971,6 @@ void on_client_update_fb_done(void* work) stream_send(client->net_stream, payload, on_write_frame_done, client); DTRACE_PROBE1(neatvnc, send_fb_done, client); - client->needs_whole_frame = false; } else { client->is_updating = false; vec_destroy(frame); @@ -962,7 +978,6 @@ void on_client_update_fb_done(void* work) } client->n_pending_requests--; - process_fb_update_requests(client); DTRACE_PROBE1(neatvnc, update_fb_done, client); @@ -973,7 +988,7 @@ void on_client_update_fb_done(void* work) int schedule_client_update_fb(struct nvnc_client* client) { - struct nvnc_fb* fb = client->server->frame; + struct nvnc_fb* fb = client->server->buffer; assert(fb); DTRACE_PROBE1(neatvnc, update_fb_start, client); @@ -1028,31 +1043,24 @@ pixfmt_failure: } EXPORT -int nvnc_feed_frame(struct nvnc* self, struct nvnc_fb* fb, - const struct pixman_region16* damage) +void nvnc_damage_region(struct nvnc* self, const struct pixman_region16* damage) { struct nvnc_client* client; - if (self->frame) - nvnc_fb_unref(self->frame); + LIST_FOREACH(client, &self->clients, link) + if (client->net_stream->state != STREAM_STATE_CLOSED) + pixman_region_union(&client->damage, &client->damage, + (struct pixman_region16*)damage); +} - self->frame = fb; - nvnc_fb_ref(self->frame); - - struct nvnc_client* tmp; - LIST_FOREACH_SAFE (client, &self->clients, link, tmp) { - if (client->net_stream->state == STREAM_STATE_CLOSED) - continue; - - pixman_region_union(&client->damage, &client->damage, - (struct pixman_region16*)damage); - pixman_region_intersect_rect(&client->damage, &client->damage, - 0, 0, fb->width, fb->height); - - process_fb_update_requests(client); - } - - return 0; +EXPORT +void nvnc_damage_whole(struct nvnc* self) +{ + struct pixman_region16 damage; + pixman_region_init_rect(&damage, 0, 0, self->display.width, + self->display.height); + nvnc_damage_region(self, &damage); + pixman_region_fini(&damage); } EXPORT @@ -1093,6 +1101,12 @@ void nvnc_set_new_client_fn(struct nvnc* self, nvnc_client_fn fn) self->new_client_fn = fn; } +EXPORT +void nvnc_set_render_fn(struct nvnc* self, nvnc_render_fn fn) +{ + self->render_fn = fn; +} + EXPORT void nvnc_set_client_cleanup_fn(struct nvnc_client* self, nvnc_client_fn fn) { @@ -1108,6 +1122,16 @@ void nvnc_set_dimensions(struct nvnc* self, uint16_t width, uint16_t height, self->display.pixfmt = fourcc_format; } +EXPORT +void nvnc_set_buffer(struct nvnc* self, struct nvnc_fb* fb) +{ + if (self->buffer) + nvnc_fb_unref(self->buffer); + + self->buffer = fb; + nvnc_fb_ref(fb); +} + EXPORT struct nvnc* nvnc_get_server(const struct nvnc_client* client) {