Blame src/grd-vnc-pipewire-stream.c

Packit Service 85e68b
/*
Packit Service 85e68b
 * Copyright (C) 2018 Red Hat Inc.
Packit Service 85e68b
 *
Packit Service 85e68b
 * This program is free software; you can redistribute it and/or
Packit Service 85e68b
 * modify it under the terms of the GNU General Public License as
Packit Service 85e68b
 * published by the Free Software Foundation; either version 2 of the
Packit Service 85e68b
 * License, or (at your option) any later version.
Packit Service 85e68b
 *
Packit Service 85e68b
 * This program is distributed in the hope that it will be useful, but
Packit Service 85e68b
 * WITHOUT ANY WARRANTY; without even the implied warranty of
Packit Service 85e68b
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Packit Service 85e68b
 * General Public License for more details.
Packit Service 85e68b
 *
Packit Service 85e68b
 * You should have received a copy of the GNU General Public License
Packit Service 85e68b
 * along with this program; if not, write to the Free Software
Packit Service 85e68b
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
Packit Service 85e68b
 * 02111-1307, USA.
Packit Service 85e68b
 *
Packit Service 85e68b
 */
Packit Service 85e68b
Packit Service 85e68b
#include "config.h"
Packit Service 85e68b
Packit Service 85e68b
#include "grd-vnc-pipewire-stream.h"
Packit Service 85e68b
Packit Service 85e68b
#include <linux/dma-buf.h>
Packit Service 85e68b
#include <pipewire/pipewire.h>
Packit Service 85e68b
#include <spa/param/props.h>
Packit Service 85e68b
#include <spa/param/format-utils.h>
Packit Service 85e68b
#include <spa/param/video/format-utils.h>
Packit Service 85e68b
#include <spa/utils/result.h>
Packit Service 85e68b
#include <sys/ioctl.h>
Packit Service 85e68b
#include <sys/mman.h>
Packit Service 85e68b
#include <sys/syscall.h>
Packit Service 85e68b
Packit Service 85e68b
#include "grd-vnc-cursor.h"
Packit Service 85e68b
Packit Service 85e68b
enum
Packit Service 85e68b
{
Packit Service 85e68b
  CLOSED,
Packit Service 85e68b
Packit Service 85e68b
  N_SIGNALS
Packit Service 85e68b
};
Packit Service 85e68b
Packit Service 85e68b
guint signals[N_SIGNALS];
Packit Service 85e68b
Packit Service 85e68b
typedef struct _GrdPipeWireSource
Packit Service 85e68b
{
Packit Service 85e68b
  GSource base;
Packit Service 85e68b
Packit Service 85e68b
  struct pw_loop *pipewire_loop;
Packit Service 85e68b
} GrdPipeWireSource;
Packit Service 85e68b
Packit Service 85e68b
typedef struct _GrdVncFrame
Packit Service 85e68b
{
Packit Service 85e68b
  void *data;
Packit Service 85e68b
  rfbCursorPtr rfb_cursor;
Packit Service 85e68b
  gboolean cursor_moved;
Packit Service 85e68b
  int cursor_x;
Packit Service 85e68b
  int cursor_y;
Packit Service 85e68b
} GrdVncFrame;
Packit Service 85e68b
Packit Service 85e68b
struct _GrdVncPipeWireStream
Packit Service 85e68b
{
Packit Service 85e68b
  GObject parent;
Packit Service 85e68b
Packit Service 85e68b
  GrdSessionVnc *session;
Packit Service 85e68b
Packit Service 85e68b
  GSource *pipewire_source;
Packit Service 85e68b
  struct pw_context *pipewire_context;
Packit Service 85e68b
  struct pw_core *pipewire_core;
Packit Service 85e68b
Packit Service 85e68b
  struct spa_hook pipewire_core_listener;
Packit Service 85e68b
Packit Service 85e68b
  GMutex frame_mutex;
Packit Service 85e68b
  GrdVncFrame *pending_frame;
Packit Service 85e68b
Packit Service 85e68b
  struct pw_stream *pipewire_stream;
Packit Service 85e68b
  struct spa_hook pipewire_stream_listener;
Packit Service 85e68b
Packit Service 85e68b
  uint32_t src_node_id;
Packit Service 85e68b
Packit Service 85e68b
  struct spa_video_info_raw spa_format;
Packit Service 85e68b
};
Packit Service 85e68b
Packit Service 85e68b
G_DEFINE_TYPE (GrdVncPipeWireStream, grd_vnc_pipewire_stream,
Packit Service 85e68b
               G_TYPE_OBJECT)
Packit Service 85e68b
Packit Service 85e68b
#define CURSOR_META_SIZE(width, height) \
Packit Service 85e68b
 (sizeof(struct spa_meta_cursor) + \
Packit Service 85e68b
  sizeof(struct spa_meta_bitmap) + width * height * 4)
Packit Service 85e68b
Packit Service 85e68b
static gboolean
Packit Service 85e68b
pipewire_loop_source_prepare (GSource *base,
Packit Service 85e68b
                              int     *timeout)
Packit Service 85e68b
{
Packit Service 85e68b
  *timeout = -1;
Packit Service 85e68b
  return FALSE;
Packit Service 85e68b
}
Packit Service 85e68b
Packit Service 85e68b
static gboolean
Packit Service 85e68b
pipewire_loop_source_dispatch (GSource     *source,
Packit Service 85e68b
                               GSourceFunc  callback,
Packit Service 85e68b
                               gpointer     user_data)
Packit Service 85e68b
{
Packit Service 85e68b
  GrdPipeWireSource *pipewire_source = (GrdPipeWireSource *) source;
Packit Service 85e68b
  int result;
Packit Service 85e68b
Packit Service 85e68b
  result = pw_loop_iterate (pipewire_source->pipewire_loop, 0);
Packit Service 85e68b
  if (result < 0)
Packit Service 85e68b
    g_warning ("pipewire_loop_iterate failed: %s", spa_strerror (result));
Packit Service 85e68b
Packit Service 85e68b
  return TRUE;
Packit Service 85e68b
}
Packit Service 85e68b
Packit Service 85e68b
static void
Packit Service 85e68b
pipewire_loop_source_finalize (GSource *source)
Packit Service 85e68b
{
Packit Service 85e68b
  GrdPipeWireSource *pipewire_source = (GrdPipeWireSource *) source;
Packit Service 85e68b
Packit Service 85e68b
  pw_loop_leave (pipewire_source->pipewire_loop);
Packit Service 85e68b
  pw_loop_destroy (pipewire_source->pipewire_loop);
Packit Service 85e68b
}
Packit Service 85e68b
Packit Service 85e68b
static GSourceFuncs pipewire_source_funcs =
Packit Service 85e68b
{
Packit Service 85e68b
  pipewire_loop_source_prepare,
Packit Service 85e68b
  NULL,
Packit Service 85e68b
  pipewire_loop_source_dispatch,
Packit Service 85e68b
  pipewire_loop_source_finalize
Packit Service 85e68b
};
Packit Service 85e68b
Packit Service 85e68b
static GrdPipeWireSource *
Packit Service 85e68b
create_pipewire_source (void)
Packit Service 85e68b
{
Packit Service 85e68b
  GrdPipeWireSource *pipewire_source;
Packit Service 85e68b
Packit Service 85e68b
  pipewire_source =
Packit Service 85e68b
    (GrdPipeWireSource *) g_source_new (&pipewire_source_funcs,
Packit Service 85e68b
                                        sizeof (GrdPipeWireSource));
Packit Service 85e68b
  pipewire_source->pipewire_loop = pw_loop_new (NULL);
Packit Service 85e68b
  if (!pipewire_source->pipewire_loop)
Packit Service 85e68b
    {
Packit Service 85e68b
      g_source_destroy ((GSource *) pipewire_source);
Packit Service 85e68b
      return NULL;
Packit Service 85e68b
    }
Packit Service 85e68b
Packit Service 85e68b
  g_source_add_unix_fd (&pipewire_source->base,
Packit Service 85e68b
                        pw_loop_get_fd (pipewire_source->pipewire_loop),
Packit Service 85e68b
                        G_IO_IN | G_IO_ERR);
Packit Service 85e68b
Packit Service 85e68b
  pw_loop_enter (pipewire_source->pipewire_loop);
Packit Service 85e68b
  g_source_attach (&pipewire_source->base, NULL);
Packit Service 85e68b
Packit Service 85e68b
  return pipewire_source;
Packit Service 85e68b
}
Packit Service 85e68b
Packit Service 85e68b
static void
Packit Service 85e68b
on_stream_state_changed (void                 *user_data,
Packit Service 85e68b
                         enum pw_stream_state  old,
Packit Service 85e68b
                         enum pw_stream_state  state,
Packit Service 85e68b
                         const char           *error)
Packit Service 85e68b
{
Packit Service 85e68b
  g_debug ("Pipewire stream state changed from %s to %s",
Packit Service 85e68b
           pw_stream_state_as_string (old),
Packit Service 85e68b
           pw_stream_state_as_string (state));
Packit Service 85e68b
Packit Service 85e68b
  switch (state)
Packit Service 85e68b
    {
Packit Service 85e68b
    case PW_STREAM_STATE_ERROR:
Packit Service 85e68b
      g_warning ("PipeWire stream error: %s", error);
Packit Service 85e68b
      break;
Packit Service 85e68b
    case PW_STREAM_STATE_PAUSED:
Packit Service 85e68b
    case PW_STREAM_STATE_STREAMING:
Packit Service 85e68b
    case PW_STREAM_STATE_UNCONNECTED:
Packit Service 85e68b
    case PW_STREAM_STATE_CONNECTING:
Packit Service 85e68b
      break;
Packit Service 85e68b
    }
Packit Service 85e68b
}
Packit Service 85e68b
Packit Service 85e68b
static void
Packit Service 85e68b
on_stream_param_changed (void                 *user_data,
Packit Service 85e68b
                         uint32_t              id,
Packit Service 85e68b
                         const struct spa_pod *format)
Packit Service 85e68b
{
Packit Service 85e68b
  GrdVncPipeWireStream *stream = GRD_VNC_PIPEWIRE_STREAM (user_data);
Packit Service 85e68b
  uint8_t params_buffer[1024];
Packit Service 85e68b
  struct spa_pod_builder pod_builder;
Packit Service 85e68b
  int width;
Packit Service 85e68b
  int height;
Packit Service 85e68b
  const struct spa_pod *params[3];
Packit Service 85e68b
Packit Service 85e68b
  if (!format || id != SPA_PARAM_Format)
Packit Service 85e68b
    return;
Packit Service 85e68b
Packit Service 85e68b
  spa_format_video_raw_parse (format, &stream->spa_format);
Packit Service 85e68b
Packit Service 85e68b
  pod_builder = SPA_POD_BUILDER_INIT (params_buffer, sizeof (params_buffer));
Packit Service 85e68b
Packit Service 85e68b
  width = stream->spa_format.size.width;
Packit Service 85e68b
  height = stream->spa_format.size.height;
Packit Service 85e68b
Packit Service 85e68b
  grd_session_vnc_queue_resize_framebuffer (stream->session, width, height);
Packit Service 85e68b
Packit Service 85e68b
  params[0] = spa_pod_builder_add_object (
Packit Service 85e68b
    &pod_builder,
Packit Service 85e68b
    SPA_TYPE_OBJECT_ParamBuffers, SPA_PARAM_Buffers,
Packit Service 85e68b
    SPA_PARAM_BUFFERS_buffers, SPA_POD_CHOICE_RANGE_Int (8, 1, 8),
Packit Service 85e68b
    0);
Packit Service 85e68b
Packit Service 85e68b
  params[1] = spa_pod_builder_add_object (
Packit Service 85e68b
    &pod_builder,
Packit Service 85e68b
    SPA_TYPE_OBJECT_ParamMeta, SPA_PARAM_Meta,
Packit Service 85e68b
    SPA_PARAM_META_type, SPA_POD_Id (SPA_META_Header),
Packit Service 85e68b
    SPA_PARAM_META_size, SPA_POD_Int (sizeof (struct spa_meta_header)),
Packit Service 85e68b
    0);
Packit Service 85e68b
Packit Service 85e68b
  params[2] = spa_pod_builder_add_object(
Packit Service 85e68b
    &pod_builder,
Packit Service 85e68b
    SPA_TYPE_OBJECT_ParamMeta, SPA_PARAM_Meta,
Packit Service 85e68b
    SPA_PARAM_META_type, SPA_POD_Id (SPA_META_Cursor),
Packit Service 85e68b
    SPA_PARAM_META_size, SPA_POD_CHOICE_RANGE_Int (CURSOR_META_SIZE (64,64),
Packit Service 85e68b
                                                   CURSOR_META_SIZE (1,1),
Packit Service 85e68b
                                                   CURSOR_META_SIZE (256,256)),
Packit Service 85e68b
    0);
Packit Service 85e68b
Packit Service 85e68b
  pw_stream_update_params (stream->pipewire_stream,
Packit Service 85e68b
                           params, G_N_ELEMENTS (params));
Packit Service 85e68b
}
Packit Service 85e68b
Packit Service 85e68b
static gboolean
Packit Service 85e68b
spa_pixel_format_to_grd_pixel_format (uint32_t        spa_format,
Packit Service 85e68b
                                      GrdPixelFormat *out_format)
Packit Service 85e68b
{
Packit Service 85e68b
  if (spa_format == SPA_VIDEO_FORMAT_RGBA)
Packit Service 85e68b
    *out_format = GRD_PIXEL_FORMAT_RGBA8888;
Packit Service 85e68b
  else
Packit Service 85e68b
    return FALSE;
Packit Service 85e68b
Packit Service 85e68b
  return TRUE;
Packit Service 85e68b
}
Packit Service 85e68b
Packit Service 85e68b
static void
Packit Service 85e68b
sync_dma_buf (int      fd,
Packit Service 85e68b
              uint64_t start_or_end)
Packit Service 85e68b
{
Packit Service 85e68b
  struct dma_buf_sync sync = { 0 };
Packit Service 85e68b
Packit Service 85e68b
  sync.flags = start_or_end | DMA_BUF_SYNC_READ;
Packit Service 85e68b
Packit Service 85e68b
  while (TRUE)
Packit Service 85e68b
    {
Packit Service 85e68b
      int ret;
Packit Service 85e68b
Packit Service 85e68b
      ret = ioctl (fd, DMA_BUF_IOCTL_SYNC, &sync);
Packit Service 85e68b
      if (ret == -1 && errno == EINTR)
Packit Service 85e68b
        {
Packit Service 85e68b
          continue;
Packit Service 85e68b
        }
Packit Service 85e68b
      else if (ret == -1)
Packit Service 85e68b
        {
Packit Service 85e68b
          g_warning ("Failed to synchronize DMA buffer: %s",
Packit Service 85e68b
                     g_strerror (errno));
Packit Service 85e68b
          break;
Packit Service 85e68b
        }
Packit Service 85e68b
      else
Packit Service 85e68b
        {
Packit Service 85e68b
          break;
Packit Service 85e68b
        }
Packit Service 85e68b
    }
Packit Service 85e68b
}
Packit Service 85e68b
Packit Service 85e68b
static int
Packit Service 85e68b
do_render (struct spa_loop *loop,
Packit Service 85e68b
           bool             async,
Packit Service 85e68b
           uint32_t         seq,
Packit Service 85e68b
           const void      *data,
Packit Service 85e68b
           size_t           size,
Packit Service 85e68b
           void            *user_data)
Packit Service 85e68b
{
Packit Service 85e68b
  GrdVncPipeWireStream *stream = GRD_VNC_PIPEWIRE_STREAM (user_data);
Packit Service 85e68b
  GrdVncFrame *frame;
Packit Service 85e68b
Packit Service 85e68b
  g_mutex_lock (&stream->frame_mutex);
Packit Service 85e68b
  frame = g_steal_pointer (&stream->pending_frame);
Packit Service 85e68b
  g_mutex_unlock (&stream->frame_mutex);
Packit Service 85e68b
Packit Service 85e68b
  if (!frame)
Packit Service 85e68b
    return 0;
Packit Service 85e68b
Packit Service 85e68b
  if (frame->rfb_cursor)
Packit Service 85e68b
    grd_session_vnc_set_cursor (stream->session, frame->rfb_cursor);
Packit Service 85e68b
Packit Service 85e68b
  if (frame->cursor_moved)
Packit Service 85e68b
    {
Packit Service 85e68b
      grd_session_vnc_move_cursor (stream->session,
Packit Service 85e68b
                                   frame->cursor_x,
Packit Service 85e68b
                                   frame->cursor_y);
Packit Service 85e68b
    }
Packit Service 85e68b
Packit Service 85e68b
  if (frame->data)
Packit Service 85e68b
    grd_session_vnc_take_buffer (stream->session, frame->data);
rpm-build 47d5e1
  else
rpm-build 47d5e1
    grd_session_vnc_flush (stream->session);
Packit Service 85e68b
Packit Service 85e68b
  g_free (frame);
Packit Service 85e68b
Packit Service 85e68b
  return 0;
Packit Service 85e68b
}
Packit Service 85e68b
Packit Service 85e68b
static GrdVncFrame *
Packit Service 85e68b
process_buffer (GrdVncPipeWireStream *stream,
Packit Service 85e68b
                struct spa_buffer    *buffer)
Packit Service 85e68b
{
Packit Service 85e68b
  size_t size;
Packit Service 85e68b
  uint8_t *map;
Packit Service 85e68b
  void *src_data;
Packit Service 85e68b
  struct spa_meta_cursor *spa_meta_cursor;
Packit Service 85e68b
  g_autofree GrdVncFrame *frame = NULL;
Packit Service 85e68b
Packit Service 85e68b
  frame = g_new0 (GrdVncFrame, 1);
Packit Service 85e68b
Packit Service 85e68b
  if (buffer->datas[0].chunk->size == 0)
Packit Service 85e68b
    {
rpm-build 47d5e1
      map = NULL;
rpm-build 47d5e1
      src_data = NULL;
Packit Service 85e68b
    }
Packit Service 85e68b
  else if (buffer->datas[0].type == SPA_DATA_MemFd)
Packit Service 85e68b
    {
Packit Service 85e68b
      size = buffer->datas[0].maxsize + buffer->datas[0].mapoffset;
Packit Service 85e68b
      map = mmap (NULL, size, PROT_READ, MAP_PRIVATE, buffer->datas[0].fd, 0);
rpm-build 60d84b
      if (map == MAP_FAILED)
rpm-build 60d84b
        {
rpm-build 60d84b
          g_warning ("Failed to mmap buffer: %s", g_strerror (errno));
rpm-build 60d84b
          return NULL;
rpm-build 60d84b
	}
Packit Service 85e68b
      src_data = SPA_MEMBER (map, buffer->datas[0].mapoffset, uint8_t);
Packit Service 85e68b
    }
Packit Service 85e68b
  else if (buffer->datas[0].type == SPA_DATA_DmaBuf)
Packit Service 85e68b
    {
Packit Service 85e68b
      int fd;
Packit Service 85e68b
Packit Service 85e68b
      fd = buffer->datas[0].fd;
Packit Service 85e68b
      size = buffer->datas[0].maxsize + buffer->datas[0].mapoffset;
Packit Service 85e68b
Packit Service 85e68b
      map = mmap (NULL, size, PROT_READ, MAP_PRIVATE, fd, 0);
rpm-build 60d84b
      if (map == MAP_FAILED)
rpm-build 60d84b
        {
rpm-build 60d84b
          g_warning ("Failed to mmap DMA buffer: %s", g_strerror (errno));
rpm-build 60d84b
          return NULL;
rpm-build 60d84b
	}
Packit Service 85e68b
      sync_dma_buf (fd, DMA_BUF_SYNC_START);
Packit Service 85e68b
Packit Service 85e68b
      src_data = SPA_MEMBER (map, buffer->datas[0].mapoffset, uint8_t);
Packit Service 85e68b
    }
Packit Service 85e68b
  else if (buffer->datas[0].type == SPA_DATA_MemPtr)
Packit Service 85e68b
    {
Packit Service 85e68b
      size = buffer->datas[0].maxsize + buffer->datas[0].mapoffset;
Packit Service 85e68b
      map = NULL;
Packit Service 85e68b
      src_data = buffer->datas[0].data;
Packit Service 85e68b
    }
Packit Service 85e68b
  else
Packit Service 85e68b
    {
Packit Service 85e68b
      return NULL;
Packit Service 85e68b
    }
Packit Service 85e68b
rpm-build 4acd8c
  if (src_data)
rpm-build ef508a
    {
rpm-build 4acd8c
      int src_stride;
rpm-build 4acd8c
      int dst_stride;
rpm-build 4acd8c
      int height;
rpm-build 47d5e1
      int y;
rpm-build 4acd8c
rpm-build 4acd8c
      src_stride = buffer->datas[0].chunk->stride;
rpm-build 4acd8c
      dst_stride = grd_session_vnc_get_framebuffer_stride (stream->session);
rpm-build 4acd8c
      height = stream->spa_format.size.height;
rpm-build 4acd8c
rpm-build 4acd8c
      frame->data = g_malloc (height * dst_stride);
rpm-build 4acd8c
      for (y = 0; y < height; y++)
rpm-build 4acd8c
        {
rpm-build 4acd8c
          memcpy (((uint8_t *) frame->data) + y * dst_stride,
rpm-build 4acd8c
                  ((uint8_t *) src_data) + y * src_stride,
rpm-build 4acd8c
                  dst_stride);
rpm-build 4acd8c
        }
rpm-build ef508a
    }
Packit Service 85e68b
Packit Service 85e68b
  if (map)
Packit Service 85e68b
    {
Packit Service 85e68b
      if (buffer->datas[0].type == SPA_DATA_DmaBuf)
Packit Service 85e68b
        sync_dma_buf (buffer->datas[0].fd, DMA_BUF_SYNC_END);
Packit Service 85e68b
      munmap (map, size);
Packit Service 85e68b
    }
Packit Service 85e68b
Packit Service 85e68b
  spa_meta_cursor = spa_buffer_find_meta_data (buffer, SPA_META_Cursor,
Packit Service 85e68b
                                               sizeof *spa_meta_cursor);
Packit Service 85e68b
  if (spa_meta_cursor && spa_meta_cursor_is_valid (spa_meta_cursor))
Packit Service 85e68b
    {
Packit Service 85e68b
      struct spa_meta_bitmap *spa_meta_bitmap;
Packit Service 85e68b
      GrdPixelFormat format;
Packit Service 85e68b
Packit Service 85e68b
      if (spa_meta_cursor->bitmap_offset)
Packit Service 85e68b
        {
Packit Service 85e68b
          spa_meta_bitmap = SPA_MEMBER (spa_meta_cursor,
Packit Service 85e68b
                                        spa_meta_cursor->bitmap_offset,
Packit Service 85e68b
                                        struct spa_meta_bitmap);
Packit Service 85e68b
        }
Packit Service 85e68b
      else
Packit Service 85e68b
        {
Packit Service 85e68b
          spa_meta_bitmap = NULL;
Packit Service 85e68b
        }
Packit Service 85e68b
Packit Service 85e68b
      if (spa_meta_bitmap &&
Packit Service 85e68b
          spa_meta_bitmap->size.width > 0 &&
Packit Service 85e68b
          spa_meta_bitmap->size.height > 0 &&
Packit Service 85e68b
          spa_pixel_format_to_grd_pixel_format (spa_meta_bitmap->format,
Packit Service 85e68b
                                                &format))
Packit Service 85e68b
        {
Packit Service 85e68b
          uint8_t *buf;
Packit Service 85e68b
          rfbCursorPtr rfb_cursor;
Packit Service 85e68b
Packit Service 85e68b
          buf = SPA_MEMBER (spa_meta_bitmap, spa_meta_bitmap->offset, uint8_t);
Packit Service 85e68b
          rfb_cursor = grd_vnc_create_cursor (spa_meta_bitmap->size.width,
Packit Service 85e68b
                                              spa_meta_bitmap->size.height,
Packit Service 85e68b
                                              spa_meta_bitmap->stride,
Packit Service 85e68b
                                              format,
Packit Service 85e68b
                                              buf);
Packit Service 85e68b
          rfb_cursor->xhot = spa_meta_cursor->hotspot.x;
Packit Service 85e68b
          rfb_cursor->yhot = spa_meta_cursor->hotspot.y;
Packit Service 85e68b
Packit Service 85e68b
          frame->rfb_cursor = rfb_cursor;
Packit Service 85e68b
        }
Packit Service 85e68b
      else if (spa_meta_bitmap)
Packit Service 85e68b
        {
Packit Service 85e68b
          frame->rfb_cursor = grd_vnc_create_empty_cursor (1, 1);
Packit Service 85e68b
        }
Packit Service 85e68b
Packit Service 85e68b
      frame->cursor_moved = TRUE;
Packit Service 85e68b
      frame->cursor_x = spa_meta_cursor->position.x;
Packit Service 85e68b
      frame->cursor_y = spa_meta_cursor->position.y;
Packit Service 85e68b
    }
Packit Service 85e68b
Packit Service 85e68b
  return g_steal_pointer (&frame);
Packit Service 85e68b
}
Packit Service 85e68b
Packit Service 85e68b
static void
Packit Service 85e68b
on_stream_process (void *user_data)
Packit Service 85e68b
{
Packit Service 85e68b
  GrdVncPipeWireStream *stream = GRD_VNC_PIPEWIRE_STREAM (user_data);
Packit Service 85e68b
  GrdPipeWireSource *pipewire_source =
Packit Service 85e68b
    (GrdPipeWireSource *) stream->pipewire_source;
Packit Service 85e68b
  struct pw_buffer *next_buffer;
Packit Service 85e68b
  struct pw_buffer *buffer = NULL;
Packit Service 85e68b
  GrdVncFrame *frame;
Packit Service 85e68b
Packit Service 85e68b
  next_buffer = pw_stream_dequeue_buffer (stream->pipewire_stream);
Packit Service 85e68b
  while (next_buffer)
Packit Service 85e68b
    {
Packit Service 85e68b
      buffer = next_buffer;
Packit Service 85e68b
      next_buffer = pw_stream_dequeue_buffer (stream->pipewire_stream);
Packit Service 85e68b
Packit Service 85e68b
      if (next_buffer)
Packit Service 85e68b
        pw_stream_queue_buffer (stream->pipewire_stream, buffer);
Packit Service 85e68b
    }
Packit Service 85e68b
  if (!buffer)
Packit Service 85e68b
    return;
Packit Service 85e68b
Packit Service 85e68b
  frame = process_buffer (stream, buffer->buffer);
Packit Service 85e68b
Packit Service 85e68b
  g_mutex_lock (&stream->frame_mutex);
Packit Service 85e68b
  if (stream->pending_frame)
Packit Service 85e68b
    {
Packit Service 85e68b
      g_free (stream->pending_frame->data);
Packit Service 85e68b
      g_clear_pointer (&stream->pending_frame, g_free);
Packit Service 85e68b
    }
Packit Service 85e68b
  stream->pending_frame = frame;
Packit Service 85e68b
  g_mutex_unlock (&stream->frame_mutex);
Packit Service 85e68b
Packit Service 85e68b
  pw_stream_queue_buffer (stream->pipewire_stream, buffer);
Packit Service 85e68b
Packit Service 85e68b
  pw_loop_invoke (pipewire_source->pipewire_loop, do_render,
Packit Service 85e68b
                  SPA_ID_INVALID, NULL, 0,
Packit Service 85e68b
                  false, stream);
Packit Service 85e68b
}
Packit Service 85e68b
Packit Service 85e68b
static const struct pw_stream_events stream_events = {
Packit Service 85e68b
  PW_VERSION_STREAM_EVENTS,
Packit Service 85e68b
  .state_changed = on_stream_state_changed,
Packit Service 85e68b
  .param_changed = on_stream_param_changed,
Packit Service 85e68b
  .process = on_stream_process,
Packit Service 85e68b
};
Packit Service 85e68b
Packit Service 85e68b
static gboolean
Packit Service 85e68b
connect_to_stream (GrdVncPipeWireStream  *stream,
Packit Service 85e68b
                   GError               **error)
Packit Service 85e68b
{
Packit Service 85e68b
  struct pw_stream *pipewire_stream;
Packit Service 85e68b
  uint8_t params_buffer[1024];
Packit Service 85e68b
  struct spa_pod_builder pod_builder;
Packit Service 85e68b
  struct spa_rectangle min_rect;
Packit Service 85e68b
  struct spa_rectangle max_rect;
Packit Service 85e68b
  struct spa_fraction min_framerate;
Packit Service 85e68b
  struct spa_fraction max_framerate;
Packit Service 85e68b
  const struct spa_pod *params[2];
Packit Service 85e68b
  int ret;
Packit Service 85e68b
Packit Service 85e68b
  pipewire_stream = pw_stream_new (stream->pipewire_core,
Packit Service 85e68b
                                   "grd-vnc-pipewire-stream",
Packit Service 85e68b
                                   NULL);
Packit Service 85e68b
Packit Service 85e68b
  min_rect = SPA_RECTANGLE (1, 1);
Packit Service 85e68b
  max_rect = SPA_RECTANGLE (INT32_MAX, INT32_MAX);
Packit Service 85e68b
  min_framerate = SPA_FRACTION (1, 1);
Packit Service 85e68b
  max_framerate = SPA_FRACTION (30, 1);
Packit Service 85e68b
Packit Service 85e68b
  pod_builder = SPA_POD_BUILDER_INIT (params_buffer, sizeof (params_buffer));
Packit Service 85e68b
  params[0] = spa_pod_builder_add_object (
Packit Service 85e68b
    &pod_builder,
Packit Service 85e68b
    SPA_TYPE_OBJECT_Format, SPA_PARAM_EnumFormat,
Packit Service 85e68b
    SPA_FORMAT_mediaType, SPA_POD_Id (SPA_MEDIA_TYPE_video),
Packit Service 85e68b
    SPA_FORMAT_mediaSubtype, SPA_POD_Id (SPA_MEDIA_SUBTYPE_raw),
Packit Service 85e68b
    SPA_FORMAT_VIDEO_format, SPA_POD_Id (SPA_VIDEO_FORMAT_BGRx),
Packit Service 85e68b
    SPA_FORMAT_VIDEO_size, SPA_POD_CHOICE_RANGE_Rectangle (&min_rect,
Packit Service 85e68b
                                                           &min_rect,
Packit Service 85e68b
                                                           &max_rect),
Packit Service 85e68b
    SPA_FORMAT_VIDEO_framerate, SPA_POD_Fraction (&SPA_FRACTION(0, 1)),
Packit Service 85e68b
    SPA_FORMAT_VIDEO_maxFramerate, SPA_POD_CHOICE_RANGE_Fraction (&min_framerate,
Packit Service 85e68b
                                                                  &min_framerate,
Packit Service 85e68b
                                                                  &max_framerate),
Packit Service 85e68b
    0);
Packit Service 85e68b
Packit Service 85e68b
  stream->pipewire_stream = pipewire_stream;
Packit Service 85e68b
Packit Service 85e68b
  pw_stream_add_listener (pipewire_stream,
Packit Service 85e68b
                          &stream->pipewire_stream_listener,
Packit Service 85e68b
                          &stream_events,
Packit Service 85e68b
                          stream);
Packit Service 85e68b
Packit Service 85e68b
  ret = pw_stream_connect (stream->pipewire_stream,
Packit Service 85e68b
                           PW_DIRECTION_INPUT,
Packit Service 85e68b
                           stream->src_node_id,
Packit Service 85e68b
                           (PW_STREAM_FLAG_RT_PROCESS |
Packit Service 85e68b
                            PW_STREAM_FLAG_AUTOCONNECT),
Packit Service 85e68b
                           params, 1);
Packit Service 85e68b
  if (ret < 0)
Packit Service 85e68b
    {
Packit Service 85e68b
      g_set_error_literal (error, G_IO_ERROR, g_io_error_from_errno (-ret),
Packit Service 85e68b
                           strerror (-ret));
Packit Service 85e68b
      return FALSE;
Packit Service 85e68b
    }
Packit Service 85e68b
Packit Service 85e68b
  return TRUE;
Packit Service 85e68b
}
Packit Service 85e68b
Packit Service 85e68b
static void
Packit Service 85e68b
on_core_error (void       *user_data,
Packit Service 85e68b
               uint32_t    id,
Packit Service 85e68b
               int         seq,
Packit Service 85e68b
               int         res,
Packit Service 85e68b
               const char *message)
Packit Service 85e68b
{
Packit Service 85e68b
  GrdVncPipeWireStream *stream = GRD_VNC_PIPEWIRE_STREAM (user_data);
Packit Service 85e68b
Packit Service 85e68b
  g_warning ("Pipewire core error: id:%u %s", id, message);
Packit Service 85e68b
Packit Service 85e68b
  if (id == PW_ID_CORE && res == -EPIPE)
Packit Service 85e68b
    g_signal_emit (stream, signals[CLOSED], 0);
Packit Service 85e68b
}
Packit Service 85e68b
Packit Service 85e68b
static const struct pw_core_events core_events = {
Packit Service 85e68b
  PW_VERSION_CORE_EVENTS,
Packit Service 85e68b
  .error = on_core_error,
Packit Service 85e68b
};
Packit Service 85e68b
Packit Service 85e68b
GrdVncPipeWireStream *
Packit Service 85e68b
grd_vnc_pipewire_stream_new (GrdSessionVnc  *session_vnc,
Packit Service 85e68b
                             uint32_t        src_node_id,
Packit Service 85e68b
                             GError        **error)
Packit Service 85e68b
{
Packit Service 85e68b
  g_autoptr (GrdVncPipeWireStream) stream = NULL;
Packit Service 85e68b
  GrdPipeWireSource *pipewire_source;
Packit Service 85e68b
  static gboolean is_pipewire_initialized = FALSE;
Packit Service 85e68b
Packit Service 85e68b
  if (!is_pipewire_initialized)
Packit Service 85e68b
    {
Packit Service 85e68b
      pw_init (NULL, NULL);
Packit Service 85e68b
      is_pipewire_initialized = TRUE;
Packit Service 85e68b
    }
Packit Service 85e68b
Packit Service 85e68b
  stream = g_object_new (GRD_TYPE_VNC_PIPEWIRE_STREAM, NULL);
Packit Service 85e68b
  stream->session = session_vnc;
Packit Service 85e68b
  stream->src_node_id = src_node_id;
Packit Service 85e68b
Packit Service 85e68b
  pipewire_source = create_pipewire_source ();
Packit Service 85e68b
  if (!pipewire_source)
Packit Service 85e68b
    {
Packit Service 85e68b
      g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
Packit Service 85e68b
                   "Failed to create PipeWire source");
Packit Service 85e68b
      return NULL;
Packit Service 85e68b
    }
Packit Service 85e68b
  stream->pipewire_source = (GSource *) pipewire_source;
Packit Service 85e68b
Packit Service 85e68b
  stream->pipewire_context = pw_context_new (pipewire_source->pipewire_loop,
Packit Service 85e68b
                                             NULL, 0);
Packit Service 85e68b
  if (!stream->pipewire_context)
Packit Service 85e68b
    {
Packit Service 85e68b
      g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
Packit Service 85e68b
                   "Failed to create pipewire context");
Packit Service 85e68b
      return NULL;
Packit Service 85e68b
    }
Packit Service 85e68b
Packit Service 85e68b
  stream->pipewire_core = pw_context_connect (stream->pipewire_context, NULL, 0);
Packit Service 85e68b
  if (!stream->pipewire_core)
Packit Service 85e68b
    {
Packit Service 85e68b
      g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
Packit Service 85e68b
                   "Failed to connect pipewire context");
Packit Service 85e68b
      return NULL;
Packit Service 85e68b
    }
Packit Service 85e68b
Packit Service 85e68b
  pw_core_add_listener (stream->pipewire_core,
Packit Service 85e68b
                        &stream->pipewire_core_listener,
Packit Service 85e68b
                        &core_events,
Packit Service 85e68b
                        stream);
Packit Service 85e68b
Packit Service 85e68b
  if (!connect_to_stream (stream, error))
Packit Service 85e68b
    return NULL;
Packit Service 85e68b
Packit Service 85e68b
  return g_steal_pointer (&stream);
Packit Service 85e68b
}
Packit Service 85e68b
Packit Service 85e68b
static void
Packit Service 85e68b
grd_vnc_pipewire_stream_finalize (GObject *object)
Packit Service 85e68b
{
Packit Service 85e68b
  GrdVncPipeWireStream *stream = GRD_VNC_PIPEWIRE_STREAM (object);
Packit Service 85e68b
Packit Service 85e68b
  /*
Packit Service 85e68b
   * We can't clear stream->pipewire_stream before destroying it, as the data
Packit Service 85e68b
   * thread in PipeWire might access the variable during destruction.
Packit Service 85e68b
   */
Packit Service 85e68b
  if (stream->pipewire_stream)
Packit Service 85e68b
    pw_stream_destroy (stream->pipewire_stream);
Packit Service 85e68b
Packit Service 85e68b
  g_clear_pointer (&stream->pipewire_core, pw_core_disconnect);
Packit Service 85e68b
  g_clear_pointer (&stream->pipewire_context, pw_context_destroy);
Packit Service 85e68b
  g_clear_pointer (&stream->pipewire_source, g_source_destroy);
Packit Service 85e68b
Packit Service 85e68b
  G_OBJECT_CLASS (grd_vnc_pipewire_stream_parent_class)->finalize (object);
Packit Service 85e68b
}
Packit Service 85e68b
Packit Service 85e68b
static void
Packit Service 85e68b
grd_vnc_pipewire_stream_init (GrdVncPipeWireStream *stream)
Packit Service 85e68b
{
Packit Service 85e68b
  g_mutex_init (&stream->frame_mutex);
Packit Service 85e68b
}
Packit Service 85e68b
Packit Service 85e68b
static void
Packit Service 85e68b
grd_vnc_pipewire_stream_class_init (GrdVncPipeWireStreamClass *klass)
Packit Service 85e68b
{
Packit Service 85e68b
  GObjectClass *object_class = G_OBJECT_CLASS (klass);
Packit Service 85e68b
Packit Service 85e68b
  object_class->finalize = grd_vnc_pipewire_stream_finalize;
Packit Service 85e68b
Packit Service 85e68b
  signals[CLOSED] = g_signal_new ("closed",
Packit Service 85e68b
                                  G_TYPE_FROM_CLASS (klass),
Packit Service 85e68b
                                  G_SIGNAL_RUN_LAST,
Packit Service 85e68b
                                  0,
Packit Service 85e68b
                                  NULL, NULL, NULL,
Packit Service 85e68b
                                  G_TYPE_NONE, 0);
Packit Service 85e68b
}