+/* Lease transition
+ * Wait for a client to update the DRM framebuffer on the CRTC managed by
+ * a lease. Once the framebuffer has been updated, it is safe to close
+ * the fd associated with the previous lease client, freeing the previous
+ * framebuffer if there are no other references to it. */
+static void wait_for_fb_update(struct lease *lease, uint32_t old_fb)
+{
+ uint32_t current_fb = old_fb;
+
+ struct pollfd drm_poll = {
+ .fd = lease->lease_fd,
+ .events = POLLIN,
+ };
+
+ while (current_fb == old_fb) {
+ drmModeCrtcPtr crtc;
+ if (poll(&drm_poll, 1, -1) < 0) {
+ if (errno == EINTR)
+ continue;
+ break;
+ }
+
+ crtc = drmModeGetCrtc(lease->lease_fd, lease->crtc_id);
+ current_fb = crtc->buffer_id;
+ drmModeFreeCrtc(crtc);
+ }
+}
+
+struct transition_ctx {
+ struct lease *lease;
+ int close_fd;
+ uint32_t old_fb;
+};
+
+static void transition_done(void *arg)
+{
+ struct transition_ctx *ctx = arg;
+ close(ctx->close_fd);
+ free(ctx);
+}
+
+static void *finish_transition_task(void *arg)
+{
+ struct transition_ctx *ctx = arg;
+ pthread_cleanup_push(transition_done, ctx);
+ wait_for_fb_update(ctx->lease, ctx->old_fb);
+ pthread_cleanup_pop(true);
+ return NULL;
+}
+
+static void close_after_lease_transition(struct lease *lease, int close_fd)
+{
+ struct transition_ctx *ctx = calloc(1, sizeof(*ctx));
+
+ assert(ctx);
+
+ drmModeCrtcPtr crtc = drmModeGetCrtc(lease->lease_fd, lease->crtc_id);
+
+ ctx->lease = lease;
+ ctx->close_fd = close_fd;
+ ctx->old_fb = crtc->buffer_id;
+
+ drmModeFreeCrtc(crtc);
+
+ int ret = pthread_create(&lease->transition_tid, NULL,
+ finish_transition_task, ctx);
+
+ lease->transition_running = (ret == 0);
+}
+
+static void cancel_lease_transition_thread(struct lease *lease)
+{
+
+ if (lease->transition_running) {
+ pthread_cancel(lease->transition_tid);
+ pthread_join(lease->transition_tid, NULL);
+ }
+
+ lease->transition_running = false;
+}
+