From 789ae171e081bd442682bcdcbca9ae900cf54f58 Mon Sep 17 00:00:00 2001 From: Pascal Nowack Date: Tue, 20 Dec 2022 16:20:42 +0100 Subject: [PATCH 1/3] rdp-pipewire-stream: Ignore frame data, when mapping memfd fails Since the mapping operation failed, the frame data is invalid. So, ignore it by using FALSE as success value for on_frame_ready. --- src/grd-rdp-pipewire-stream.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/grd-rdp-pipewire-stream.c b/src/grd-rdp-pipewire-stream.c index 11146635..c225d7a1 100644 --- a/src/grd-rdp-pipewire-stream.c +++ b/src/grd-rdp-pipewire-stream.c @@ -584,7 +584,7 @@ process_frame_data (GrdRdpPipeWireStream *stream, if (map == MAP_FAILED) { g_warning ("Failed to mmap buffer: %s", g_strerror (errno)); - callback (stream, g_steal_pointer (&frame), TRUE, user_data); + callback (stream, g_steal_pointer (&frame), FALSE, user_data); return; } src_data = SPA_MEMBER (map, buffer->datas[0].mapoffset, uint8_t); -- GitLab From f67d191892f2a77408f49570fbaee4d1bbba6194 Mon Sep 17 00:00:00 2001 From: Pascal Nowack Date: Wed, 30 Nov 2022 14:26:10 +0100 Subject: [PATCH 2/3] rdp-pipewire-stream: Decouple pointer updates from frame updates When handling PipeWire buffer contents, it is possible that a frame contains both frame data and pointer data. In such case, the frame data might need to be processed on the EGL thread. In this particular situation, the pointer data is handled first in the PipeWire data thread and then a task is pushed into the queue of the EGL thread. While the frame data is processed on the EGL thread, a new pointer update can occur and be directly handled. If the frame data of the previous PipeWire buffer was now finished processing, both frame data and pointer data of that PipeWire buffer are now pushed to the actual frame handling of the respective backend. The frame data is now new, but the pointer data is not and should be dropped. This currently results into old pointer updates replacing newer updates. To handle this situation, simply decouple pointer updates from frame updates. For that, add a separate GSource for the pointer data, where it is handled alone. --- src/grd-rdp-pipewire-stream.c | 265 +++++++++++++++++++--------------- 1 file changed, 150 insertions(+), 115 deletions(-) diff --git a/src/grd-rdp-pipewire-stream.c b/src/grd-rdp-pipewire-stream.c index c225d7a1..d61b931b 100644 --- a/src/grd-rdp-pipewire-stream.c +++ b/src/grd-rdp-pipewire-stream.c @@ -67,18 +67,19 @@ struct _GrdRdpFrame size_t map_size; uint8_t *map; - gboolean has_pointer_data; + GrdRdpPipeWireStream *stream; + GrdRdpFrameReadyCallback callback; + gpointer callback_user_data; +}; + +typedef struct +{ uint8_t *pointer_bitmap; uint16_t pointer_hotspot_x; uint16_t pointer_hotspot_y; uint16_t pointer_width; uint16_t pointer_height; - gboolean pointer_is_hidden; - - GrdRdpPipeWireStream *stream; - GrdRdpFrameReadyCallback callback; - gpointer callback_user_data; -}; +} RdpPointer; typedef struct { @@ -118,10 +119,14 @@ struct _GrdRdpPipeWireStream GrdRdpBufferPool *buffer_pool; - GSource *render_source; + GSource *frame_render_source; GMutex frame_mutex; GrdRdpFrame *pending_frame; + GSource *pointer_render_source; + GMutex pointer_mutex; + RdpPointer *pending_pointer; + struct pw_stream *pipewire_stream; struct spa_hook pipewire_stream_listener; @@ -169,11 +174,17 @@ grd_rdp_frame_unref (GrdRdpFrame *frame) g_assert (!frame->has_map); g_clear_pointer (&frame->buffer, grd_rdp_buffer_release); - g_free (frame->pointer_bitmap); g_free (frame); } } +static void +rdp_pointer_free (RdpPointer *rdp_pointer) +{ + g_clear_pointer (&rdp_pointer->pointer_bitmap, g_free); + g_free (rdp_pointer); +} + void grd_rdp_pipewire_stream_resize (GrdRdpPipeWireStream *stream, GrdRdpVirtualMonitor *virtual_monitor) @@ -199,7 +210,7 @@ grd_rdp_pipewire_stream_resize (GrdRdpPipeWireStream *stream, } static gboolean -do_render (gpointer user_data) +render_frame (gpointer user_data) { GrdRdpPipeWireStream *stream = GRD_RDP_PIPEWIRE_STREAM (user_data); GrdRdpFrame *frame; @@ -223,21 +234,40 @@ do_render (gpointer user_data) grd_session_rdp_maybe_encode_new_frame (stream->session_rdp, stream->rdp_surface); - if (frame->pointer_bitmap) + grd_rdp_frame_unref (frame); + + return G_SOURCE_CONTINUE; +} + +static gboolean +render_mouse_pointer (gpointer user_data) +{ + GrdRdpPipeWireStream *stream = user_data; + g_autoptr (GMutexLocker) locker = NULL; + RdpPointer *rdp_pointer; + + locker = g_mutex_locker_new (&stream->pointer_mutex); + if (!stream->pending_pointer) + return G_SOURCE_CONTINUE; + + rdp_pointer = g_steal_pointer (&stream->pending_pointer); + g_clear_pointer (&locker, g_mutex_locker_free); + + if (rdp_pointer->pointer_bitmap) { grd_session_rdp_update_pointer (stream->session_rdp, - frame->pointer_hotspot_x, - frame->pointer_hotspot_y, - frame->pointer_width, - frame->pointer_height, - g_steal_pointer (&frame->pointer_bitmap)); + rdp_pointer->pointer_hotspot_x, + rdp_pointer->pointer_hotspot_y, + rdp_pointer->pointer_width, + rdp_pointer->pointer_height, + g_steal_pointer (&rdp_pointer->pointer_bitmap)); } - else if (frame->pointer_is_hidden) + else { grd_session_rdp_hide_pointer (stream->session_rdp); } - grd_rdp_frame_unref (frame); + rdp_pointer_free (rdp_pointer); return G_SOURCE_CONTINUE; } @@ -258,13 +288,22 @@ static GSourceFuncs render_source_funcs = }; static void -create_render_source (GrdRdpPipeWireStream *stream, - GMainContext *render_context) +create_render_sources (GrdRdpPipeWireStream *stream, + GMainContext *render_context) { - stream->render_source = g_source_new (&render_source_funcs, sizeof (GSource)); - g_source_set_callback (stream->render_source, do_render, stream, NULL); - g_source_set_ready_time (stream->render_source, -1); - g_source_attach (stream->render_source, render_context); + stream->frame_render_source = g_source_new (&render_source_funcs, + sizeof (GSource)); + g_source_set_callback (stream->frame_render_source, + render_frame, stream, NULL); + g_source_set_ready_time (stream->frame_render_source, -1); + g_source_attach (stream->frame_render_source, render_context); + + stream->pointer_render_source = g_source_new (&render_source_funcs, + sizeof (GSource)); + g_source_set_callback (stream->pointer_render_source, + render_mouse_pointer, stream, NULL); + g_source_set_ready_time (stream->pointer_render_source, -1); + g_source_attach (stream->pointer_render_source, render_context); } static void @@ -405,11 +444,11 @@ on_stream_param_changed (void *user_data, static void process_mouse_pointer_bitmap (GrdRdpPipeWireStream *stream, - struct spa_buffer *buffer, - GrdRdpFrame *frame) + struct spa_buffer *buffer) { struct spa_meta_cursor *spa_meta_cursor; struct spa_meta_bitmap *spa_meta_bitmap; + RdpPointer *rdp_pointer = NULL; GrdPixelFormat format; spa_meta_cursor = spa_buffer_find_meta_data (buffer, SPA_META_Cursor, @@ -432,20 +471,71 @@ process_mouse_pointer_bitmap (GrdRdpPipeWireStream *stream, uint8_t *buf; buf = SPA_MEMBER (spa_meta_bitmap, spa_meta_bitmap->offset, uint8_t); - frame->pointer_bitmap = - g_memdup2 (buf, spa_meta_bitmap->size.height * - spa_meta_bitmap->stride); - frame->pointer_hotspot_x = spa_meta_cursor->hotspot.x; - frame->pointer_hotspot_y = spa_meta_cursor->hotspot.y; - frame->pointer_width = spa_meta_bitmap->size.width; - frame->pointer_height = spa_meta_bitmap->size.height; - frame->has_pointer_data = TRUE; + + rdp_pointer = g_new0 (RdpPointer, 1); + rdp_pointer->pointer_bitmap = + g_memdup2 (buf, spa_meta_bitmap->size.height * spa_meta_bitmap->stride); + rdp_pointer->pointer_hotspot_x = spa_meta_cursor->hotspot.x; + rdp_pointer->pointer_hotspot_y = spa_meta_cursor->hotspot.y; + rdp_pointer->pointer_width = spa_meta_bitmap->size.width; + rdp_pointer->pointer_height = spa_meta_bitmap->size.height; } else if (spa_meta_bitmap) { - frame->pointer_is_hidden = TRUE; - frame->has_pointer_data = TRUE; + rdp_pointer = g_new0 (RdpPointer, 1); + } + + if (rdp_pointer) + { + g_mutex_lock (&stream->pointer_mutex); + g_clear_pointer (&stream->pending_pointer, rdp_pointer_free); + + stream->pending_pointer = rdp_pointer; + g_mutex_unlock (&stream->pointer_mutex); + + g_source_set_ready_time (stream->pointer_render_source, 0); + } +} + +static void +on_frame_ready (GrdRdpPipeWireStream *stream, + GrdRdpFrame *frame, + gboolean success, + gpointer user_data) +{ + struct pw_buffer *buffer = user_data; + GrdRdpFrame *pending_frame; + + g_assert (frame); + + if (frame->has_map) + { + munmap (frame->map, frame->map_size); + frame->has_map = FALSE; + } + + if (!success) + goto out; + + g_mutex_lock (&stream->frame_mutex); + pending_frame = g_steal_pointer (&stream->pending_frame); + if (pending_frame) + { + if (!frame->buffer && pending_frame->buffer) + frame->buffer = g_steal_pointer (&pending_frame->buffer); + + grd_rdp_frame_unref (pending_frame); } + stream->pending_frame = g_steal_pointer (&frame); + g_mutex_unlock (&stream->frame_mutex); + +out: + if (buffer) + pw_stream_queue_buffer (stream->pipewire_stream, buffer); + + g_source_set_ready_time (stream->frame_render_source, 0); + + g_clear_pointer (&frame, grd_rdp_frame_unref); } static void @@ -542,11 +632,12 @@ on_framebuffer_ready (gboolean success, static void process_frame_data (GrdRdpPipeWireStream *stream, - struct spa_buffer *buffer, - GrdRdpFrame *frame) + struct pw_buffer *pw_buffer) { - GrdRdpFrameReadyCallback callback = frame->callback; - gpointer user_data = frame->callback_user_data; + struct spa_buffer *buffer = pw_buffer->buffer; + g_autoptr (GrdRdpFrame) frame = NULL; + GrdRdpFrameReadyCallback callback; + gpointer user_data; uint32_t drm_format; int bpp; int width; @@ -564,6 +655,10 @@ process_frame_data (GrdRdpPipeWireStream *stream, grd_get_spa_format_details (stream->spa_format.format, &drm_format, &bpp); + frame = grd_rdp_frame_new (stream, on_frame_ready, pw_buffer); + callback = frame->callback; + user_data = frame->callback_user_data; + if (buffer->datas[0].type == SPA_DATA_MemFd) { GrdSession *session = GRD_SESSION (stream->session_rdp); @@ -741,69 +836,10 @@ process_frame_data (GrdRdpPipeWireStream *stream, } } -static void -take_pointer_data_from (GrdRdpFrame *src_frame, - GrdRdpFrame *dst_frame) -{ - g_assert (!dst_frame->pointer_bitmap); - dst_frame->pointer_bitmap = g_steal_pointer (&src_frame->pointer_bitmap); - - dst_frame->pointer_hotspot_x = src_frame->pointer_hotspot_x; - dst_frame->pointer_hotspot_y = src_frame->pointer_hotspot_y; - dst_frame->pointer_width = src_frame->pointer_width; - dst_frame->pointer_height = src_frame->pointer_height; - dst_frame->pointer_is_hidden = src_frame->pointer_is_hidden; - dst_frame->has_pointer_data = TRUE; -} - -static void -on_frame_ready (GrdRdpPipeWireStream *stream, - GrdRdpFrame *frame, - gboolean success, - gpointer user_data) -{ - struct pw_buffer *buffer = user_data; - GrdRdpFrame *pending_frame; - - g_assert (frame); - - if (frame->has_map) - { - munmap (frame->map, frame->map_size); - frame->has_map = FALSE; - } - - if (!success) - goto out; - - g_mutex_lock (&stream->frame_mutex); - pending_frame = g_steal_pointer (&stream->pending_frame); - if (pending_frame) - { - if (!frame->buffer && pending_frame->buffer) - frame->buffer = g_steal_pointer (&pending_frame->buffer); - if (!frame->has_pointer_data && pending_frame->has_pointer_data) - take_pointer_data_from (pending_frame, frame); - - grd_rdp_frame_unref (pending_frame); - } - stream->pending_frame = g_steal_pointer (&frame); - g_mutex_unlock (&stream->frame_mutex); - -out: - if (buffer) - pw_stream_queue_buffer (stream->pipewire_stream, buffer); - - g_source_set_ready_time (stream->render_source, 0); - - g_clear_pointer (&frame, grd_rdp_frame_unref); -} - static void on_stream_process (void *user_data) { GrdRdpPipeWireStream *stream = GRD_RDP_PIPEWIRE_STREAM (user_data); - g_autoptr (GrdRdpFrame) frame = NULL; struct pw_buffer *last_pointer_buffer = NULL; struct pw_buffer *last_frame_buffer = NULL; struct pw_buffer *next_buffer; @@ -848,25 +884,16 @@ on_stream_process (void *user_data) if (!last_pointer_buffer && !last_frame_buffer) return; - frame = grd_rdp_frame_new (stream, on_frame_ready, last_frame_buffer); if (last_pointer_buffer) { - process_mouse_pointer_bitmap (stream, last_pointer_buffer->buffer, frame); + process_mouse_pointer_bitmap (stream, last_pointer_buffer->buffer); if (last_pointer_buffer != last_frame_buffer) pw_stream_queue_buffer (stream->pipewire_stream, last_pointer_buffer); } - if (!last_frame_buffer) - { - GrdRdpFrameReadyCallback callback = frame->callback; - gpointer callback_user_data = frame->callback_user_data; - - callback (stream, g_steal_pointer (&frame), TRUE, callback_user_data); - return; - } + return; - process_frame_data (stream, last_frame_buffer->buffer, - g_steal_pointer (&frame)); + process_frame_data (stream, last_frame_buffer); } static const struct pw_stream_events stream_events = { @@ -1074,7 +1101,7 @@ grd_rdp_pipewire_stream_new (GrdSessionRdp *session_rdp, pw_init (NULL, NULL); - create_render_source (stream, render_context); + create_render_sources (stream, render_context); pipewire_source = grd_attached_pipewire_source_new ("RDP", error); if (!pipewire_source) @@ -1143,18 +1170,25 @@ grd_rdp_pipewire_stream_finalize (GObject *object) g_clear_pointer (&stream->pipewire_source, g_source_unref); } - if (stream->render_source) + if (stream->pointer_render_source) + { + g_source_destroy (stream->pointer_render_source); + g_clear_pointer (&stream->pointer_render_source, g_source_unref); + } + if (stream->frame_render_source) { - g_source_destroy (stream->render_source); - g_clear_pointer (&stream->render_source, g_source_unref); + g_source_destroy (stream->frame_render_source); + g_clear_pointer (&stream->frame_render_source, g_source_unref); } grd_rdp_damage_detector_invalidate_surface (stream->rdp_surface->detector); + g_clear_pointer (&stream->pending_pointer, rdp_pointer_free); g_clear_pointer (&stream->pending_frame, grd_rdp_frame_unref); release_all_buffers (stream); g_clear_object (&stream->buffer_pool); + g_mutex_clear (&stream->pointer_mutex); g_mutex_clear (&stream->frame_mutex); pw_deinit (); @@ -1166,6 +1200,7 @@ static void grd_rdp_pipewire_stream_init (GrdRdpPipeWireStream *stream) { g_mutex_init (&stream->frame_mutex); + g_mutex_init (&stream->pointer_mutex); } static void -- GitLab From c899b6c4045503b1980bc5df6dfd61c21d06bba9 Mon Sep 17 00:00:00 2001 From: Pascal Nowack Date: Wed, 30 Nov 2022 15:52:13 +0100 Subject: [PATCH 3/3] vnc-pipewire-stream: Decouple pointer updates from frame updates When handling PipeWire buffer contents, it is possible that a frame contains both frame data and pointer data. In such case, the frame data might need to be processed on the EGL thread. In this particular situation, the pointer data is handled first in the PipeWire data thread and then a task is pushed into the queue of the EGL thread. While the frame data is processed on the EGL thread, a new pointer update can occur and be directly handled. If the frame data of the previous PipeWire buffer was now finished processing, both frame data and pointer data of that PipeWire buffer are now pushed to the actual frame handling of the respective backend. The frame data is now new, but the pointer data is not and should be dropped. This currently results into old pointer updates replacing newer updates. To handle this situation, simply decouple pointer updates from frame updates. For that, add a separate GSource for the pointer data, where it is handled alone. --- src/grd-vnc-pipewire-stream.c | 240 +++++++++++++++++++++------------- 1 file changed, 146 insertions(+), 94 deletions(-) diff --git a/src/grd-vnc-pipewire-stream.c b/src/grd-vnc-pipewire-stream.c index bd789df3..36a3c6ff 100644 --- a/src/grd-vnc-pipewire-stream.c +++ b/src/grd-vnc-pipewire-stream.c @@ -55,16 +55,20 @@ struct _GrdVncFrame gatomicrefcount refcount; void *data; - rfbCursorPtr rfb_cursor; - gboolean cursor_moved; - int cursor_x; - int cursor_y; GrdVncPipeWireStream *stream; GrdVncFrameReadyCallback callback; gpointer callback_user_data; }; +typedef struct +{ + rfbCursorPtr rfb_cursor; + gboolean cursor_moved; + int cursor_x; + int cursor_y; +} VncPointer; + struct _GrdVncPipeWireStream { GObject parent; @@ -81,6 +85,10 @@ struct _GrdVncPipeWireStream GrdVncFrame *pending_frame; GSource *pending_frame_source; + GMutex pointer_mutex; + VncPointer *pending_pointer; + GSource *pending_pointer_source; + struct pw_stream *pipewire_stream; struct spa_hook pipewire_stream_listener; @@ -96,6 +104,13 @@ static void grd_vnc_frame_unref (GrdVncFrame *frame); G_DEFINE_AUTOPTR_CLEANUP_FUNC (GrdVncFrame, grd_vnc_frame_unref) +static void +vnc_pointer_free (VncPointer *vnc_pointer) +{ + g_clear_pointer (&vnc_pointer->rfb_cursor, rfbFreeCursor); + g_free (vnc_pointer); +} + void grd_vnc_pipewire_stream_resize (GrdVncPipeWireStream *stream, GrdVncVirtualMonitor *virtual_monitor) @@ -237,13 +252,12 @@ grd_vnc_frame_unref (GrdVncFrame *frame) if (g_atomic_ref_count_dec (&frame->refcount)) { g_free (frame->data); - g_clear_pointer (&frame->rfb_cursor, rfbFreeCursor); g_free (frame); } } static gboolean -do_render (gpointer user_data) +render_frame (gpointer user_data) { GrdVncPipeWireStream *stream = GRD_VNC_PIPEWIRE_STREAM (user_data); GrdVncFrame *frame; @@ -261,19 +275,6 @@ do_render (gpointer user_data) return G_SOURCE_CONTINUE; } - if (frame->rfb_cursor) - { - grd_session_vnc_set_cursor (stream->session, - g_steal_pointer (&frame->rfb_cursor)); - } - - if (frame->cursor_moved) - { - grd_session_vnc_move_cursor (stream->session, - frame->cursor_x, - frame->cursor_y); - } - if (frame->data) { grd_session_vnc_take_buffer (stream->session, @@ -289,10 +290,43 @@ do_render (gpointer user_data) return G_SOURCE_CONTINUE; } +static gboolean +render_mouse_pointer (gpointer user_data) +{ + GrdVncPipeWireStream *stream = user_data; + g_autoptr (GMutexLocker) locker = NULL; + VncPointer *vnc_pointer; + + locker = g_mutex_locker_new (&stream->pointer_mutex); + if (!stream->pending_pointer) + return G_SOURCE_CONTINUE; + + vnc_pointer = g_steal_pointer (&stream->pending_pointer); + g_clear_pointer (&locker, g_mutex_locker_free); + + if (vnc_pointer->rfb_cursor) + { + grd_session_vnc_set_cursor (stream->session, + g_steal_pointer (&vnc_pointer->rfb_cursor)); + } + if (vnc_pointer->cursor_moved) + { + grd_session_vnc_move_cursor (stream->session, + vnc_pointer->cursor_x, + vnc_pointer->cursor_y); + } + + grd_session_vnc_flush (stream->session); + + vnc_pointer_free (vnc_pointer); + + return G_SOURCE_CONTINUE; +} + static void -process_mouse_pointer_bitmap (GrdVncPipeWireStream *stream, - struct spa_buffer *buffer, - GrdVncFrame *frame) +process_mouse_pointer_bitmap (GrdVncPipeWireStream *stream, + struct spa_buffer *buffer, + VncPointer **vnc_pointer) { struct spa_meta_cursor *spa_meta_cursor; struct spa_meta_bitmap *spa_meta_bitmap; @@ -327,12 +361,54 @@ process_mouse_pointer_bitmap (GrdVncPipeWireStream *stream, rfb_cursor->xhot = spa_meta_cursor->hotspot.x; rfb_cursor->yhot = spa_meta_cursor->hotspot.y; - frame->rfb_cursor = rfb_cursor; + if (!(*vnc_pointer)) + *vnc_pointer = g_new0 (VncPointer, 1); + (*vnc_pointer)->rfb_cursor = rfb_cursor; } else if (spa_meta_bitmap) { - frame->rfb_cursor = grd_vnc_create_empty_cursor (1, 1); + if (!(*vnc_pointer)) + *vnc_pointer = g_new0 (VncPointer, 1); + (*vnc_pointer)->rfb_cursor = grd_vnc_create_empty_cursor (1, 1); + } +} + +static void +on_frame_ready (GrdVncPipeWireStream *stream, + GrdVncFrame *frame, + gboolean success, + gpointer user_data) +{ + GrdVncFrame *pending_frame; + struct pw_buffer *buffer = user_data; + + g_assert (frame); + + if (!success) + goto out; + + g_mutex_lock (&stream->frame_mutex); + + pending_frame = g_steal_pointer (&stream->pending_frame); + if (pending_frame) + { + if (!frame->data && pending_frame->data) + frame->data = g_steal_pointer (&pending_frame->data); + + grd_vnc_frame_unref (pending_frame); } + + stream->pending_frame = g_steal_pointer (&frame); + + g_mutex_unlock (&stream->frame_mutex); + +out: + if (buffer) + pw_stream_queue_buffer (stream->pipewire_stream, buffer); + + g_source_set_ready_time (stream->pending_frame_source, 0); + + g_clear_pointer (&frame, grd_vnc_frame_unref); } static void @@ -369,11 +445,12 @@ on_dma_buf_downloaded (gboolean success, static void process_frame_data (GrdVncPipeWireStream *stream, - struct spa_buffer *buffer, - GrdVncFrame *frame) + struct pw_buffer *pw_buffer) { - GrdVncFrameReadyCallback callback = frame->callback; - gpointer user_data = frame->callback_user_data; + struct spa_buffer *buffer = pw_buffer->buffer; + g_autoptr (GrdVncFrame) frame = NULL; + GrdVncFrameReadyCallback callback; + gpointer user_data; int dst_stride; uint32_t drm_format; int bpp; @@ -389,6 +466,10 @@ process_frame_data (GrdVncPipeWireStream *stream, grd_get_spa_format_details (stream->spa_format.format, &drm_format, &bpp); + frame = grd_vnc_frame_new (stream, on_frame_ready, pw_buffer); + callback = frame->callback; + user_data = frame->callback_user_data; + if (buffer->datas[0].type == SPA_DATA_MemFd) { size_t size; @@ -473,66 +554,20 @@ process_frame_data (GrdVncPipeWireStream *stream, } static gboolean -pending_frame_source_dispatch (GSource *source, - GSourceFunc callback, - gpointer user_data) +render_source_dispatch (GSource *source, + GSourceFunc callback, + gpointer user_data) { g_source_set_ready_time (source, -1); return callback (user_data); } -static GSourceFuncs pending_frame_source_funcs = +static GSourceFuncs render_source_funcs = { - .dispatch = pending_frame_source_dispatch, + .dispatch = render_source_dispatch, }; -static void -on_frame_ready (GrdVncPipeWireStream *stream, - GrdVncFrame *frame, - gboolean success, - gpointer user_data) -{ - GrdVncFrame *pending_frame; - struct pw_buffer *buffer = user_data; - - g_assert (frame); - - if (!success) - goto out; - - g_mutex_lock (&stream->frame_mutex); - - pending_frame = g_steal_pointer (&stream->pending_frame); - if (pending_frame) - { - if (!frame->data && pending_frame->data) - frame->data = g_steal_pointer (&pending_frame->data); - if (!frame->rfb_cursor && pending_frame->rfb_cursor) - frame->rfb_cursor = g_steal_pointer (&pending_frame->rfb_cursor); - if (!frame->cursor_moved && pending_frame->cursor_moved) - { - frame->cursor_x = pending_frame->cursor_x; - frame->cursor_y = pending_frame->cursor_y; - frame->cursor_moved = TRUE; - } - - grd_vnc_frame_unref (pending_frame); - } - - stream->pending_frame = g_steal_pointer (&frame); - - g_mutex_unlock (&stream->frame_mutex); - -out: - if (buffer) - pw_stream_queue_buffer (stream->pipewire_stream, buffer); - - g_source_set_ready_time (stream->pending_frame_source, 0); - - g_clear_pointer (&frame, grd_vnc_frame_unref); -} - static void maybe_consume_pointer_position (struct pw_buffer *buffer, gboolean *cursor_moved, @@ -559,6 +594,7 @@ on_stream_process (void *user_data) struct pw_buffer *last_pointer_buffer = NULL; struct pw_buffer *last_frame_buffer = NULL; struct pw_buffer *next_buffer; + VncPointer *vnc_pointer = NULL; gboolean cursor_moved = FALSE; int cursor_x = 0; int cursor_y = 0; @@ -606,29 +642,35 @@ on_stream_process (void *user_data) if (!last_pointer_buffer && !last_frame_buffer && !cursor_moved) return; - frame = grd_vnc_frame_new (stream, on_frame_ready, last_frame_buffer); - frame->cursor_moved = cursor_moved; - frame->cursor_x = cursor_x; - frame->cursor_y = cursor_y; + if (cursor_moved) + { + vnc_pointer = g_new0 (VncPointer, 1); + vnc_pointer->cursor_moved = cursor_moved; + vnc_pointer->cursor_x = cursor_x; + vnc_pointer->cursor_y = cursor_y; + } if (last_pointer_buffer) { - process_mouse_pointer_bitmap (stream, last_pointer_buffer->buffer, frame); + process_mouse_pointer_bitmap (stream, last_pointer_buffer->buffer, + &vnc_pointer); if (last_pointer_buffer != last_frame_buffer) pw_stream_queue_buffer (stream->pipewire_stream, last_pointer_buffer); } - - if (!last_frame_buffer) + if (vnc_pointer) { - GrdVncFrameReadyCallback callback = frame->callback; - gpointer callback_user_data = frame->callback_user_data; + g_mutex_lock (&stream->pointer_mutex); + g_clear_pointer (&stream->pending_pointer, vnc_pointer_free); - callback (stream, g_steal_pointer (&frame), TRUE, callback_user_data); - return; + stream->pending_pointer = vnc_pointer; + g_mutex_unlock (&stream->pointer_mutex); + + g_source_set_ready_time (stream->pending_pointer_source, 0); } + if (!last_frame_buffer) + return; - process_frame_data (stream, last_frame_buffer->buffer, - g_steal_pointer (&frame)); + process_frame_data (stream, last_frame_buffer); } static const struct pw_stream_events stream_events = { @@ -850,9 +892,15 @@ grd_vnc_pipewire_stream_new (GrdSessionVnc *session_vnc, return NULL; } - source = g_source_new (&pending_frame_source_funcs, sizeof (GSource)); + source = g_source_new (&render_source_funcs, sizeof (GSource)); stream->pending_frame_source = source; - g_source_set_callback (source, do_render, stream, NULL); + g_source_set_callback (source, render_frame, stream, NULL); + g_source_attach (source, NULL); + g_source_unref (source); + + source = g_source_new (&render_source_funcs, sizeof (GSource)); + stream->pending_pointer_source = source; + g_source_set_callback (source, render_mouse_pointer, stream, NULL); g_source_attach (source, NULL); g_source_unref (source); @@ -904,6 +952,7 @@ grd_vnc_pipewire_stream_finalize (GObject *object) g_clear_pointer (&stream->pipewire_core, pw_core_disconnect); g_clear_pointer (&stream->pipewire_context, pw_context_destroy); + g_clear_pointer (&stream->pending_pointer_source, g_source_destroy); g_clear_pointer (&stream->pending_frame_source, g_source_destroy); if (stream->pipewire_source) { @@ -911,8 +960,10 @@ grd_vnc_pipewire_stream_finalize (GObject *object) g_clear_pointer (&stream->pipewire_source, g_source_unref); } + g_clear_pointer (&stream->pending_pointer, vnc_pointer_free); g_clear_pointer (&stream->pending_frame, grd_vnc_frame_unref); + g_mutex_clear (&stream->pointer_mutex); g_mutex_clear (&stream->frame_mutex); pw_deinit (); @@ -924,6 +975,7 @@ static void grd_vnc_pipewire_stream_init (GrdVncPipeWireStream *stream) { g_mutex_init (&stream->frame_mutex); + g_mutex_init (&stream->pointer_mutex); } static void -- GitLab