mirror of
https://github.com/morgan9e/grd
synced 2026-04-14 00:14:18 +09:00
.
This commit is contained in:
62
grd-avc-frame-info.c
Normal file
62
grd-avc-frame-info.c
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2024 Pascal Nowack
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License as
|
||||||
|
* published by the Free Software Foundation; either version 2 of the
|
||||||
|
* License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
|
||||||
|
* 02111-1307, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
|
#include "grd-avc-frame-info.h"
|
||||||
|
|
||||||
|
struct _GrdAVCFrameInfo
|
||||||
|
{
|
||||||
|
GrdAVCFrameType frame_type;
|
||||||
|
uint8_t qp;
|
||||||
|
uint8_t quality;
|
||||||
|
};
|
||||||
|
|
||||||
|
gboolean
|
||||||
|
grd_avc_frame_info_get_frame_type (GrdAVCFrameInfo *avc_frame_info)
|
||||||
|
{
|
||||||
|
return avc_frame_info->frame_type;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t
|
||||||
|
grd_avc_frame_info_get_qp (GrdAVCFrameInfo *avc_frame_info)
|
||||||
|
{
|
||||||
|
return avc_frame_info->qp;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t
|
||||||
|
grd_avc_frame_info_get_quality_value (GrdAVCFrameInfo *avc_frame_info)
|
||||||
|
{
|
||||||
|
return avc_frame_info->quality;
|
||||||
|
}
|
||||||
|
|
||||||
|
GrdAVCFrameInfo *
|
||||||
|
grd_avc_frame_info_new (GrdAVCFrameType frame_type,
|
||||||
|
uint8_t qp,
|
||||||
|
uint8_t quality)
|
||||||
|
{
|
||||||
|
GrdAVCFrameInfo *avc_frame_info;
|
||||||
|
|
||||||
|
avc_frame_info = g_new0 (GrdAVCFrameInfo, 1);
|
||||||
|
avc_frame_info->frame_type = frame_type;
|
||||||
|
avc_frame_info->qp = qp;
|
||||||
|
avc_frame_info->quality = quality;
|
||||||
|
|
||||||
|
return avc_frame_info;
|
||||||
|
}
|
||||||
41
grd-avc-frame-info.h
Normal file
41
grd-avc-frame-info.h
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2024 Pascal Nowack
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License as
|
||||||
|
* published by the Free Software Foundation; either version 2 of the
|
||||||
|
* License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
|
||||||
|
* 02111-1307, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <glib.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#include "grd-types.h"
|
||||||
|
|
||||||
|
typedef enum
|
||||||
|
{
|
||||||
|
GRD_AVC_FRAME_TYPE_P,
|
||||||
|
GRD_AVC_FRAME_TYPE_I,
|
||||||
|
} GrdAVCFrameType;
|
||||||
|
|
||||||
|
GrdAVCFrameInfo *grd_avc_frame_info_new (GrdAVCFrameType frame_type,
|
||||||
|
uint8_t qp,
|
||||||
|
uint8_t quality);
|
||||||
|
|
||||||
|
gboolean grd_avc_frame_info_get_frame_type (GrdAVCFrameInfo *avc_frame_info);
|
||||||
|
|
||||||
|
uint8_t grd_avc_frame_info_get_qp (GrdAVCFrameInfo *avc_frame_info);
|
||||||
|
|
||||||
|
uint8_t grd_avc_frame_info_get_quality_value (GrdAVCFrameInfo *avc_frame_info);
|
||||||
79
grd-bitstream.c
Normal file
79
grd-bitstream.c
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2024 Pascal Nowack
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License as
|
||||||
|
* published by the Free Software Foundation; either version 2 of the
|
||||||
|
* License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
|
||||||
|
* 02111-1307, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
|
#include "grd-bitstream.h"
|
||||||
|
|
||||||
|
#include <glib.h>
|
||||||
|
|
||||||
|
struct _GrdBitstream
|
||||||
|
{
|
||||||
|
uint8_t *data;
|
||||||
|
uint32_t data_size;
|
||||||
|
|
||||||
|
GrdAVCFrameInfo *avc_frame_info;
|
||||||
|
};
|
||||||
|
|
||||||
|
uint8_t *
|
||||||
|
grd_bitstream_get_data (GrdBitstream *bitstream)
|
||||||
|
{
|
||||||
|
return bitstream->data;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t
|
||||||
|
grd_bitstream_get_data_size (GrdBitstream *bitstream)
|
||||||
|
{
|
||||||
|
return bitstream->data_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
GrdAVCFrameInfo *
|
||||||
|
grd_bitstream_get_avc_frame_info (GrdBitstream *bitstream)
|
||||||
|
{
|
||||||
|
return bitstream->avc_frame_info;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
grd_bitstream_set_avc_frame_info (GrdBitstream *bitstream,
|
||||||
|
GrdAVCFrameInfo *avc_frame_info)
|
||||||
|
{
|
||||||
|
g_assert (!bitstream->avc_frame_info);
|
||||||
|
|
||||||
|
bitstream->avc_frame_info = avc_frame_info;
|
||||||
|
}
|
||||||
|
|
||||||
|
GrdBitstream *
|
||||||
|
grd_bitstream_new (uint8_t *data,
|
||||||
|
uint32_t data_size)
|
||||||
|
{
|
||||||
|
GrdBitstream *bitstream;
|
||||||
|
|
||||||
|
bitstream = g_new0 (GrdBitstream, 1);
|
||||||
|
bitstream->data = data;
|
||||||
|
bitstream->data_size = data_size;
|
||||||
|
|
||||||
|
return bitstream;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
grd_bitstream_free (GrdBitstream *bitstream)
|
||||||
|
{
|
||||||
|
g_clear_pointer (&bitstream->avc_frame_info, g_free);
|
||||||
|
g_free (bitstream);
|
||||||
|
}
|
||||||
38
grd-bitstream.h
Normal file
38
grd-bitstream.h
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2024 Pascal Nowack
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License as
|
||||||
|
* published by the Free Software Foundation; either version 2 of the
|
||||||
|
* License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
|
||||||
|
* 02111-1307, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#include "grd-types.h"
|
||||||
|
|
||||||
|
GrdBitstream *grd_bitstream_new (uint8_t *data,
|
||||||
|
uint32_t data_size);
|
||||||
|
|
||||||
|
void grd_bitstream_free (GrdBitstream *bitstream);
|
||||||
|
|
||||||
|
uint8_t *grd_bitstream_get_data (GrdBitstream *bitstream);
|
||||||
|
|
||||||
|
uint32_t grd_bitstream_get_data_size (GrdBitstream *bitstream);
|
||||||
|
|
||||||
|
GrdAVCFrameInfo *grd_bitstream_get_avc_frame_info (GrdBitstream *bitstream);
|
||||||
|
|
||||||
|
void grd_bitstream_set_avc_frame_info (GrdBitstream *bitstream,
|
||||||
|
GrdAVCFrameInfo *avc_frame_info);
|
||||||
2674
grd-clipboard-rdp.c
Normal file
2674
grd-clipboard-rdp.c
Normal file
File diff suppressed because it is too large
Load Diff
54
grd-clipboard-rdp.h
Normal file
54
grd-clipboard-rdp.h
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2020 Pascal Nowack
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License as
|
||||||
|
* published by the Free Software Foundation; either version 2 of the
|
||||||
|
* License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
|
||||||
|
* 02111-1307, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <freerdp/server/cliprdr.h>
|
||||||
|
|
||||||
|
#include "grd-clipboard.h"
|
||||||
|
|
||||||
|
#define GRD_TYPE_CLIPBOARD_RDP (grd_clipboard_rdp_get_type ())
|
||||||
|
G_DECLARE_FINAL_TYPE (GrdClipboardRdp,
|
||||||
|
grd_clipboard_rdp,
|
||||||
|
GRD, CLIPBOARD_RDP,
|
||||||
|
GrdClipboard)
|
||||||
|
|
||||||
|
GrdClipboardRdp *grd_clipboard_rdp_new (GrdSessionRdp *session_rdp,
|
||||||
|
HANDLE vcm,
|
||||||
|
gboolean relieve_filename_restriction);
|
||||||
|
|
||||||
|
void grd_clipboard_rdp_lock_remote_clipboard_data (GrdClipboardRdp *clipboard_rdp,
|
||||||
|
uint32_t clip_data_id);
|
||||||
|
|
||||||
|
void grd_clipboard_rdp_unlock_remote_clipboard_data (GrdClipboardRdp *clipboard_rdp,
|
||||||
|
uint32_t clip_data_id);
|
||||||
|
|
||||||
|
void grd_clipboard_rdp_request_remote_file_size_async (GrdClipboardRdp *clipboard_rdp,
|
||||||
|
uint32_t stream_id,
|
||||||
|
uint32_t list_index,
|
||||||
|
gboolean has_clip_data_id,
|
||||||
|
uint32_t clip_data_id);
|
||||||
|
|
||||||
|
void grd_clipboard_rdp_request_remote_file_range_async (GrdClipboardRdp *clipboard_rdp,
|
||||||
|
uint32_t stream_id,
|
||||||
|
uint32_t list_index,
|
||||||
|
uint64_t offset,
|
||||||
|
uint32_t requested_size,
|
||||||
|
gboolean has_clip_data_id,
|
||||||
|
uint32_t clip_data_id);
|
||||||
208
grd-clipboard-vnc.c
Normal file
208
grd-clipboard-vnc.c
Normal file
@@ -0,0 +1,208 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2020-2021 Pascal Nowack
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License as
|
||||||
|
* published by the Free Software Foundation; either version 2 of the
|
||||||
|
* License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
|
||||||
|
* 02111-1307, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
|
#include "grd-clipboard-vnc.h"
|
||||||
|
|
||||||
|
#include "grd-session-vnc.h"
|
||||||
|
|
||||||
|
struct _GrdClipboardVnc
|
||||||
|
{
|
||||||
|
GrdClipboard parent;
|
||||||
|
|
||||||
|
GrdSessionVnc *session_vnc;
|
||||||
|
|
||||||
|
char *clipboard_utf8_string;
|
||||||
|
};
|
||||||
|
|
||||||
|
G_DEFINE_TYPE (GrdClipboardVnc, grd_clipboard_vnc, GRD_TYPE_CLIPBOARD)
|
||||||
|
|
||||||
|
static void
|
||||||
|
grd_clipboard_vnc_update_client_mime_type_list (GrdClipboard *clipboard,
|
||||||
|
GList *mime_type_list)
|
||||||
|
{
|
||||||
|
GrdClipboardVnc *clipboard_vnc = GRD_CLIPBOARD_VNC (clipboard);
|
||||||
|
gboolean found_utf8_string = FALSE;
|
||||||
|
GrdMimeType mime_type;
|
||||||
|
GList *l;
|
||||||
|
|
||||||
|
for (l = mime_type_list; l && !found_utf8_string; l = l->next)
|
||||||
|
{
|
||||||
|
mime_type = GPOINTER_TO_UINT (l->data);
|
||||||
|
switch (mime_type)
|
||||||
|
{
|
||||||
|
case GRD_MIME_TYPE_TEXT_PLAIN:
|
||||||
|
break;
|
||||||
|
case GRD_MIME_TYPE_TEXT_PLAIN_UTF8:
|
||||||
|
case GRD_MIME_TYPE_TEXT_UTF8_STRING:
|
||||||
|
found_utf8_string = TRUE;
|
||||||
|
break;
|
||||||
|
case GRD_MIME_TYPE_TEXT_HTML:
|
||||||
|
case GRD_MIME_TYPE_IMAGE_BMP:
|
||||||
|
case GRD_MIME_TYPE_IMAGE_TIFF:
|
||||||
|
case GRD_MIME_TYPE_IMAGE_GIF:
|
||||||
|
case GRD_MIME_TYPE_IMAGE_JPEG:
|
||||||
|
case GRD_MIME_TYPE_IMAGE_PNG:
|
||||||
|
case GRD_MIME_TYPE_TEXT_URILIST:
|
||||||
|
case GRD_MIME_TYPE_XS_GNOME_COPIED_FILES:
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
g_assert_not_reached ();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (found_utf8_string)
|
||||||
|
{
|
||||||
|
g_clear_pointer (&clipboard_vnc->clipboard_utf8_string, g_free);
|
||||||
|
|
||||||
|
grd_clipboard_request_server_content_for_mime_type_async (clipboard,
|
||||||
|
mime_type);
|
||||||
|
}
|
||||||
|
|
||||||
|
g_list_free (mime_type_list);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
grd_clipboard_vnc_request_client_content_for_mime_type (GrdClipboard *clipboard,
|
||||||
|
GrdMimeTypeTable *mime_type_table,
|
||||||
|
unsigned int serial)
|
||||||
|
{
|
||||||
|
GrdClipboardVnc *clipboard_vnc = GRD_CLIPBOARD_VNC (clipboard);
|
||||||
|
uint32_t size;
|
||||||
|
|
||||||
|
size = strlen (clipboard_vnc->clipboard_utf8_string);
|
||||||
|
grd_clipboard_submit_client_content_for_mime_type (
|
||||||
|
clipboard, serial, (uint8_t *) clipboard_vnc->clipboard_utf8_string, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
grd_clipboard_vnc_submit_requested_server_content (GrdClipboard *clipboard,
|
||||||
|
uint8_t *src_data,
|
||||||
|
uint32_t src_size)
|
||||||
|
{
|
||||||
|
GrdClipboardVnc *clipboard_vnc = GRD_CLIPBOARD_VNC (clipboard);
|
||||||
|
g_autoptr (GError) error = NULL;
|
||||||
|
char *dst_data;
|
||||||
|
|
||||||
|
if (!src_data)
|
||||||
|
return;
|
||||||
|
|
||||||
|
dst_data = g_convert ((char *) src_data, src_size,
|
||||||
|
"iso8859-1", "utf-8",
|
||||||
|
NULL, NULL, &error);
|
||||||
|
if (!dst_data)
|
||||||
|
{
|
||||||
|
g_warning ("[VNC.Clipboard] Failed to convert clipboard content: %s",
|
||||||
|
error->message);
|
||||||
|
g_free (src_data);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
grd_session_vnc_set_client_clipboard_text (clipboard_vnc->session_vnc,
|
||||||
|
dst_data, strlen (dst_data));
|
||||||
|
|
||||||
|
g_free (src_data);
|
||||||
|
g_free (dst_data);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
grd_clipboard_vnc_maybe_enable_clipboard (GrdClipboardVnc *clipboard_vnc)
|
||||||
|
{
|
||||||
|
grd_clipboard_maybe_enable_clipboard (GRD_CLIPBOARD (clipboard_vnc));
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
grd_clipboard_vnc_set_clipboard_text (GrdClipboardVnc *clipboard_vnc,
|
||||||
|
char *text,
|
||||||
|
int text_length)
|
||||||
|
{
|
||||||
|
GrdClipboard *clipboard = GRD_CLIPBOARD (clipboard_vnc);
|
||||||
|
g_autoptr (GError) error = NULL;
|
||||||
|
GrdMimeTypeTable *mime_type_table;
|
||||||
|
GList *mime_type_tables = NULL;
|
||||||
|
|
||||||
|
g_clear_pointer (&clipboard_vnc->clipboard_utf8_string, g_free);
|
||||||
|
|
||||||
|
clipboard_vnc->clipboard_utf8_string = g_convert (text, text_length,
|
||||||
|
"utf-8", "iso8859-1",
|
||||||
|
NULL, NULL, &error);
|
||||||
|
if (!clipboard_vnc->clipboard_utf8_string)
|
||||||
|
{
|
||||||
|
g_warning ("[VNC.Clipboard] Failed to convert clipboard content: %s",
|
||||||
|
error->message);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
mime_type_table = g_malloc0 (sizeof (GrdMimeTypeTable));
|
||||||
|
mime_type_table->mime_type = GRD_MIME_TYPE_TEXT_PLAIN_UTF8;
|
||||||
|
mime_type_tables = g_list_append (mime_type_tables, mime_type_table);
|
||||||
|
|
||||||
|
mime_type_table = g_malloc0 (sizeof (GrdMimeTypeTable));
|
||||||
|
mime_type_table->mime_type = GRD_MIME_TYPE_TEXT_UTF8_STRING;
|
||||||
|
mime_type_tables = g_list_append (mime_type_tables, mime_type_table);
|
||||||
|
|
||||||
|
grd_clipboard_update_server_mime_type_list (clipboard, mime_type_tables);
|
||||||
|
}
|
||||||
|
|
||||||
|
GrdClipboardVnc *
|
||||||
|
grd_clipboard_vnc_new (GrdSessionVnc *session_vnc)
|
||||||
|
{
|
||||||
|
GrdClipboardVnc *clipboard_vnc;
|
||||||
|
|
||||||
|
clipboard_vnc = g_object_new (GRD_TYPE_CLIPBOARD_VNC, NULL);
|
||||||
|
clipboard_vnc->session_vnc = session_vnc;
|
||||||
|
|
||||||
|
grd_clipboard_initialize (GRD_CLIPBOARD (clipboard_vnc),
|
||||||
|
GRD_SESSION (session_vnc));
|
||||||
|
|
||||||
|
return clipboard_vnc;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
grd_clipboard_vnc_dispose (GObject *object)
|
||||||
|
{
|
||||||
|
GrdClipboardVnc *clipboard_vnc = GRD_CLIPBOARD_VNC (object);
|
||||||
|
|
||||||
|
grd_clipboard_disable_clipboard (GRD_CLIPBOARD (clipboard_vnc));
|
||||||
|
g_clear_pointer (&clipboard_vnc->clipboard_utf8_string, g_free);
|
||||||
|
|
||||||
|
G_OBJECT_CLASS (grd_clipboard_vnc_parent_class)->dispose (object);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
grd_clipboard_vnc_init (GrdClipboardVnc *clipboard_vnc)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
grd_clipboard_vnc_class_init (GrdClipboardVncClass *klass)
|
||||||
|
{
|
||||||
|
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
||||||
|
GrdClipboardClass *clipboard_class = GRD_CLIPBOARD_CLASS (klass);
|
||||||
|
|
||||||
|
object_class->dispose = grd_clipboard_vnc_dispose;
|
||||||
|
|
||||||
|
clipboard_class->update_client_mime_type_list =
|
||||||
|
grd_clipboard_vnc_update_client_mime_type_list;
|
||||||
|
clipboard_class->request_client_content_for_mime_type =
|
||||||
|
grd_clipboard_vnc_request_client_content_for_mime_type;
|
||||||
|
clipboard_class->submit_requested_server_content =
|
||||||
|
grd_clipboard_vnc_submit_requested_server_content;
|
||||||
|
}
|
||||||
36
grd-clipboard-vnc.h
Normal file
36
grd-clipboard-vnc.h
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2020 Pascal Nowack
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License as
|
||||||
|
* published by the Free Software Foundation; either version 2 of the
|
||||||
|
* License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
|
||||||
|
* 02111-1307, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "grd-clipboard.h"
|
||||||
|
|
||||||
|
#define GRD_TYPE_CLIPBOARD_VNC (grd_clipboard_vnc_get_type ())
|
||||||
|
G_DECLARE_FINAL_TYPE (GrdClipboardVnc,
|
||||||
|
grd_clipboard_vnc,
|
||||||
|
GRD, CLIPBOARD_VNC,
|
||||||
|
GrdClipboard)
|
||||||
|
|
||||||
|
GrdClipboardVnc *grd_clipboard_vnc_new (GrdSessionVnc *session_vnc);
|
||||||
|
|
||||||
|
void grd_clipboard_vnc_maybe_enable_clipboard (GrdClipboardVnc *clipboard_vnc);
|
||||||
|
|
||||||
|
void grd_clipboard_vnc_set_clipboard_text (GrdClipboardVnc *clipboard_vnc,
|
||||||
|
char *text,
|
||||||
|
int text_length);
|
||||||
497
grd-clipboard.c
Normal file
497
grd-clipboard.c
Normal file
@@ -0,0 +1,497 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2020-2021 Pascal Nowack
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License as
|
||||||
|
* published by the Free Software Foundation; either version 2 of the
|
||||||
|
* License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
|
||||||
|
* 02111-1307, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
|
#include "grd-clipboard.h"
|
||||||
|
|
||||||
|
#include <gio/gio.h>
|
||||||
|
#include <gio/gunixinputstream.h>
|
||||||
|
|
||||||
|
#include "grd-session.h"
|
||||||
|
|
||||||
|
#define MAX_READ_TIME_MS 4000
|
||||||
|
|
||||||
|
typedef struct _ReadMimeTypeContentContext
|
||||||
|
{
|
||||||
|
GrdClipboard *clipboard;
|
||||||
|
int fd;
|
||||||
|
GCancellable *cancellable;
|
||||||
|
} ReadMimeTypeContentContext;
|
||||||
|
|
||||||
|
typedef struct _ReadMimeTypeContentResult
|
||||||
|
{
|
||||||
|
uint8_t *data;
|
||||||
|
uint32_t size;
|
||||||
|
} ReadMimeTypeContentResult;
|
||||||
|
|
||||||
|
typedef struct _GrdClipboardPrivate
|
||||||
|
{
|
||||||
|
GrdSession *session;
|
||||||
|
|
||||||
|
gboolean enabled;
|
||||||
|
|
||||||
|
GHashTable *client_mime_type_tables;
|
||||||
|
|
||||||
|
ReadMimeTypeContentResult *read_result;
|
||||||
|
GCancellable *read_cancellable;
|
||||||
|
unsigned int abort_read_source_id;
|
||||||
|
gboolean has_pending_read_operation;
|
||||||
|
|
||||||
|
GCond pending_read_cond;
|
||||||
|
GMutex pending_read_mutex;
|
||||||
|
} GrdClipboardPrivate;
|
||||||
|
|
||||||
|
G_DEFINE_TYPE_WITH_PRIVATE (GrdClipboard, grd_clipboard, G_TYPE_OBJECT)
|
||||||
|
|
||||||
|
static void
|
||||||
|
handle_read_result (GrdClipboard *clipboard,
|
||||||
|
ReadMimeTypeContentResult *read_result)
|
||||||
|
{
|
||||||
|
GrdClipboardClass *klass = GRD_CLIPBOARD_GET_CLASS (clipboard);
|
||||||
|
GrdClipboardPrivate *priv = grd_clipboard_get_instance_private (clipboard);
|
||||||
|
|
||||||
|
priv->has_pending_read_operation = FALSE;
|
||||||
|
|
||||||
|
/* Discard the read_result, if the clipboard is already disabled. */
|
||||||
|
if (!priv->enabled)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (read_result->data)
|
||||||
|
g_debug ("Clipboard[SelectionRead]: Request successful");
|
||||||
|
else
|
||||||
|
g_debug ("Clipboard[SelectionRead]: Request failed");
|
||||||
|
|
||||||
|
klass->submit_requested_server_content (clipboard, read_result->data,
|
||||||
|
read_result->size);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
flush_pending_read_result (GrdClipboard *clipboard)
|
||||||
|
{
|
||||||
|
GrdClipboardPrivate *priv = grd_clipboard_get_instance_private (clipboard);
|
||||||
|
ReadMimeTypeContentResult *read_result;
|
||||||
|
|
||||||
|
g_mutex_lock (&priv->pending_read_mutex);
|
||||||
|
while (!priv->read_result)
|
||||||
|
g_cond_wait (&priv->pending_read_cond, &priv->pending_read_mutex);
|
||||||
|
g_mutex_unlock (&priv->pending_read_mutex);
|
||||||
|
|
||||||
|
read_result = g_steal_pointer (&priv->read_result);
|
||||||
|
|
||||||
|
handle_read_result (clipboard, read_result);
|
||||||
|
g_free (read_result);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
abort_current_read_operation (GrdClipboard *clipboard)
|
||||||
|
{
|
||||||
|
GrdClipboardPrivate *priv = grd_clipboard_get_instance_private (clipboard);
|
||||||
|
|
||||||
|
if (!priv->has_pending_read_operation)
|
||||||
|
return;
|
||||||
|
|
||||||
|
g_debug ("Clipboard[SelectionRead]: Aborting current read operation");
|
||||||
|
g_cancellable_cancel (priv->read_cancellable);
|
||||||
|
|
||||||
|
g_clear_object (&priv->read_cancellable);
|
||||||
|
g_clear_handle_id (&priv->abort_read_source_id, g_source_remove);
|
||||||
|
|
||||||
|
flush_pending_read_result (clipboard);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
grd_clipboard_update_server_mime_type_list (GrdClipboard *clipboard,
|
||||||
|
GList *mime_type_tables)
|
||||||
|
{
|
||||||
|
GrdClipboardPrivate *priv = grd_clipboard_get_instance_private (clipboard);
|
||||||
|
GList *l;
|
||||||
|
|
||||||
|
g_debug ("Clipboard[SetSelection]: Updating servers clipboard");
|
||||||
|
for (l = mime_type_tables; l; l = l->next)
|
||||||
|
{
|
||||||
|
GrdMimeTypeTable *mime_type_table = l->data;
|
||||||
|
GrdMimeType mime_type;
|
||||||
|
|
||||||
|
mime_type = mime_type_table->mime_type;
|
||||||
|
g_debug ("Clipboard[SetSelection]: Update contains mime type %s",
|
||||||
|
grd_mime_type_to_string (mime_type));
|
||||||
|
|
||||||
|
g_hash_table_insert (priv->client_mime_type_tables,
|
||||||
|
GUINT_TO_POINTER (mime_type), mime_type_table);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!priv->enabled)
|
||||||
|
{
|
||||||
|
g_debug ("Clipboard[EnableClipboard]: Enabling clipboard");
|
||||||
|
priv->enabled = grd_session_enable_clipboard (priv->session,
|
||||||
|
clipboard, mime_type_tables);
|
||||||
|
if (priv->enabled)
|
||||||
|
g_debug ("Clipboard[EnableClipboard]: Clipboard enabled");
|
||||||
|
else
|
||||||
|
g_debug ("Clipboard[EnableClipboard]: Clipboard could not be enabled");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
abort_current_read_operation (clipboard);
|
||||||
|
|
||||||
|
if (mime_type_tables)
|
||||||
|
grd_session_set_selection (priv->session, mime_type_tables);
|
||||||
|
}
|
||||||
|
g_debug ("Clipboard[SetSelection]: Update complete");
|
||||||
|
|
||||||
|
g_list_free (mime_type_tables);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
async_read_operation_complete (GObject *source_object,
|
||||||
|
GAsyncResult *result,
|
||||||
|
gpointer user_data)
|
||||||
|
{
|
||||||
|
GrdClipboard *clipboard = user_data;
|
||||||
|
GrdClipboardPrivate *priv;
|
||||||
|
ReadMimeTypeContentContext *read_context =
|
||||||
|
g_task_get_task_data (G_TASK (result));
|
||||||
|
ReadMimeTypeContentResult *read_result;
|
||||||
|
|
||||||
|
if (g_cancellable_is_cancelled (read_context->cancellable))
|
||||||
|
return;
|
||||||
|
|
||||||
|
priv = grd_clipboard_get_instance_private (clipboard);
|
||||||
|
g_assert (priv->has_pending_read_operation);
|
||||||
|
|
||||||
|
g_clear_object (&priv->read_cancellable);
|
||||||
|
g_clear_handle_id (&priv->abort_read_source_id, g_source_remove);
|
||||||
|
|
||||||
|
read_result = g_steal_pointer (&priv->read_result);
|
||||||
|
|
||||||
|
handle_read_result (clipboard, read_result);
|
||||||
|
g_free (read_result);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
clear_read_context (gpointer data)
|
||||||
|
{
|
||||||
|
ReadMimeTypeContentContext *read_context = data;
|
||||||
|
|
||||||
|
g_object_unref (read_context->cancellable);
|
||||||
|
|
||||||
|
g_free (data);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
read_mime_type_content_in_thread (GTask *task,
|
||||||
|
gpointer source_object,
|
||||||
|
gpointer task_data,
|
||||||
|
GCancellable *cancellable)
|
||||||
|
{
|
||||||
|
ReadMimeTypeContentContext *read_context = task_data;
|
||||||
|
GrdClipboard *clipboard = read_context->clipboard;
|
||||||
|
GrdClipboardPrivate *priv = grd_clipboard_get_instance_private (clipboard);
|
||||||
|
ReadMimeTypeContentResult *read_result;
|
||||||
|
GInputStream *input_stream;
|
||||||
|
GArray *data;
|
||||||
|
gboolean success = FALSE;
|
||||||
|
g_autoptr (GError) error = NULL;
|
||||||
|
|
||||||
|
input_stream = g_unix_input_stream_new (read_context->fd, TRUE);
|
||||||
|
data = g_array_new (FALSE, TRUE, sizeof (uint8_t));
|
||||||
|
|
||||||
|
while (TRUE)
|
||||||
|
{
|
||||||
|
int len;
|
||||||
|
uint8_t buffer[1024];
|
||||||
|
|
||||||
|
len = g_input_stream_read (input_stream, buffer, G_N_ELEMENTS (buffer),
|
||||||
|
read_context->cancellable, &error);
|
||||||
|
if (len < 0)
|
||||||
|
{
|
||||||
|
g_warning ("Clipboard[SelectionRead]: Failed to read mime type "
|
||||||
|
"content: %s", error->message);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else if (len == 0)
|
||||||
|
{
|
||||||
|
success = TRUE;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
g_array_append_vals (data, buffer, len);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
read_result = g_malloc0 (sizeof (ReadMimeTypeContentResult));
|
||||||
|
if (success && data->len > 0)
|
||||||
|
{
|
||||||
|
read_result->size = data->len;
|
||||||
|
read_result->data = (uint8_t *) g_array_free (data, FALSE);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
g_array_free (data, TRUE);
|
||||||
|
}
|
||||||
|
|
||||||
|
g_object_unref (input_stream);
|
||||||
|
|
||||||
|
g_mutex_lock (&priv->pending_read_mutex);
|
||||||
|
priv->read_result = read_result;
|
||||||
|
g_cond_signal (&priv->pending_read_cond);
|
||||||
|
g_mutex_unlock (&priv->pending_read_mutex);
|
||||||
|
|
||||||
|
g_task_return_pointer (task, NULL, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
abort_mime_type_content_read (gpointer user_data)
|
||||||
|
{
|
||||||
|
GrdClipboard *clipboard = user_data;
|
||||||
|
GrdClipboardPrivate *priv = grd_clipboard_get_instance_private (clipboard);
|
||||||
|
|
||||||
|
g_debug ("Clipboard[SelectionRead]: Aborting current read operation "
|
||||||
|
"(Timeout reached)");
|
||||||
|
|
||||||
|
g_assert (priv->has_pending_read_operation);
|
||||||
|
g_assert (priv->abort_read_source_id);
|
||||||
|
|
||||||
|
priv->abort_read_source_id = 0;
|
||||||
|
abort_current_read_operation (clipboard);
|
||||||
|
|
||||||
|
return G_SOURCE_REMOVE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
read_mime_type_content_async (GrdClipboard *clipboard,
|
||||||
|
int fd)
|
||||||
|
{
|
||||||
|
GrdClipboardPrivate *priv = grd_clipboard_get_instance_private (clipboard);
|
||||||
|
ReadMimeTypeContentContext *read_context;
|
||||||
|
GTask *task;
|
||||||
|
|
||||||
|
abort_current_read_operation (clipboard);
|
||||||
|
priv->read_cancellable = g_cancellable_new ();
|
||||||
|
priv->has_pending_read_operation = TRUE;
|
||||||
|
g_assert (!priv->read_result);
|
||||||
|
|
||||||
|
read_context = g_malloc0 (sizeof (ReadMimeTypeContentContext));
|
||||||
|
read_context->clipboard = clipboard;
|
||||||
|
read_context->fd = fd;
|
||||||
|
read_context->cancellable = g_object_ref (priv->read_cancellable);
|
||||||
|
|
||||||
|
task = g_task_new (NULL, NULL, async_read_operation_complete, clipboard);
|
||||||
|
g_task_set_task_data (task, read_context, clear_read_context);
|
||||||
|
g_task_run_in_thread (task, read_mime_type_content_in_thread);
|
||||||
|
g_object_unref (task);
|
||||||
|
|
||||||
|
priv->abort_read_source_id =
|
||||||
|
g_timeout_add (MAX_READ_TIME_MS, abort_mime_type_content_read, clipboard);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
grd_clipboard_request_server_content_for_mime_type_async (GrdClipboard *clipboard,
|
||||||
|
GrdMimeType mime_type)
|
||||||
|
{
|
||||||
|
GrdClipboardClass *klass = GRD_CLIPBOARD_GET_CLASS (clipboard);
|
||||||
|
GrdClipboardPrivate *priv = grd_clipboard_get_instance_private (clipboard);
|
||||||
|
int fd;
|
||||||
|
|
||||||
|
g_return_if_fail (klass->submit_requested_server_content);
|
||||||
|
|
||||||
|
g_assert (priv->enabled);
|
||||||
|
|
||||||
|
g_debug ("Clipboard[SelectionRead]: Requesting data from servers clipboard"
|
||||||
|
" (mime type: %s)", grd_mime_type_to_string (mime_type));
|
||||||
|
fd = grd_session_selection_read (priv->session, mime_type);
|
||||||
|
if (fd == -1)
|
||||||
|
{
|
||||||
|
g_debug ("Clipboard[SelectionRead]: Request failed");
|
||||||
|
klass->submit_requested_server_content (clipboard, NULL, 0);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
read_mime_type_content_async (clipboard, fd);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
grd_clipboard_initialize (GrdClipboard *clipboard,
|
||||||
|
GrdSession *session)
|
||||||
|
{
|
||||||
|
GrdClipboardPrivate *priv = grd_clipboard_get_instance_private (clipboard);
|
||||||
|
|
||||||
|
priv->session = session;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
grd_clipboard_maybe_enable_clipboard (GrdClipboard *clipboard)
|
||||||
|
{
|
||||||
|
GrdClipboardPrivate *priv = grd_clipboard_get_instance_private (clipboard);
|
||||||
|
|
||||||
|
g_debug ("Clipboard[EnableClipboard]: Enabling clipboard");
|
||||||
|
if (priv->enabled)
|
||||||
|
{
|
||||||
|
g_debug ("Clipboard[EnableClipboard]: Clipboard already enabled");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
priv->enabled = grd_session_enable_clipboard (priv->session, clipboard, NULL);
|
||||||
|
if (priv->enabled)
|
||||||
|
g_debug ("Clipboard[EnableClipboard]: Clipboard enabled");
|
||||||
|
else
|
||||||
|
g_debug ("Clipboard[EnableClipboard]: Clipboard could not be enabled");
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
grd_clipboard_disable_clipboard (GrdClipboard *clipboard)
|
||||||
|
{
|
||||||
|
GrdClipboardPrivate *priv = grd_clipboard_get_instance_private (clipboard);
|
||||||
|
|
||||||
|
if (!priv->enabled)
|
||||||
|
return;
|
||||||
|
|
||||||
|
g_debug ("Clipboard[DisableClipboard]: Disabling clipboard");
|
||||||
|
grd_session_disable_clipboard (priv->session);
|
||||||
|
priv->enabled = FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
grd_clipboard_update_client_mime_type_list (GrdClipboard *clipboard,
|
||||||
|
GList *mime_type_list)
|
||||||
|
{
|
||||||
|
GrdClipboardClass *klass = GRD_CLIPBOARD_GET_CLASS (clipboard);
|
||||||
|
GrdClipboardPrivate *priv = grd_clipboard_get_instance_private (clipboard);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ensure that the response with the read mime type content is sent to the
|
||||||
|
* client first, before sending the new mime type list
|
||||||
|
*/
|
||||||
|
abort_current_read_operation (clipboard);
|
||||||
|
|
||||||
|
g_assert (priv->enabled);
|
||||||
|
|
||||||
|
if (!klass->update_client_mime_type_list)
|
||||||
|
return;
|
||||||
|
|
||||||
|
g_hash_table_remove_all (priv->client_mime_type_tables);
|
||||||
|
|
||||||
|
g_debug ("Clipboard[SelectionOwnerChanged]: Updating clients clipboard");
|
||||||
|
klass->update_client_mime_type_list (clipboard, mime_type_list);
|
||||||
|
g_debug ("Clipboard[SelectionOwnerChanged]: Update complete");
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
grd_clipboard_submit_client_content_for_mime_type (GrdClipboard *clipboard,
|
||||||
|
unsigned int serial,
|
||||||
|
const uint8_t *data,
|
||||||
|
uint32_t size)
|
||||||
|
{
|
||||||
|
GrdClipboardPrivate *priv = grd_clipboard_get_instance_private (clipboard);
|
||||||
|
|
||||||
|
g_assert (priv->enabled);
|
||||||
|
|
||||||
|
if (data && size)
|
||||||
|
g_debug ("Clipboard[SelectionTransfer]: Request for serial %u was successful", serial);
|
||||||
|
else
|
||||||
|
g_debug ("Clipboard[SelectionTransfer]: Request for serial %u failed", serial);
|
||||||
|
|
||||||
|
grd_session_selection_write (priv->session, serial, data, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
grd_clipboard_request_client_content_for_mime_type (GrdClipboard *clipboard,
|
||||||
|
GrdMimeType mime_type,
|
||||||
|
unsigned int serial)
|
||||||
|
{
|
||||||
|
GrdClipboardClass *klass = GRD_CLIPBOARD_GET_CLASS (clipboard);
|
||||||
|
GrdClipboardPrivate *priv = grd_clipboard_get_instance_private (clipboard);
|
||||||
|
GrdMimeTypeTable *mime_type_table = NULL;
|
||||||
|
|
||||||
|
g_assert (priv->enabled);
|
||||||
|
|
||||||
|
g_return_if_fail (klass->request_client_content_for_mime_type);
|
||||||
|
|
||||||
|
g_debug ("Clipboard[SelectionTransfer]: Requesting data from clients clipboard"
|
||||||
|
" (mime type: %s, serial: %u)",
|
||||||
|
grd_mime_type_to_string (mime_type), serial);
|
||||||
|
mime_type_table = g_hash_table_lookup (priv->client_mime_type_tables,
|
||||||
|
GUINT_TO_POINTER (mime_type));
|
||||||
|
if (!mime_type_table)
|
||||||
|
{
|
||||||
|
grd_clipboard_submit_client_content_for_mime_type (clipboard, serial,
|
||||||
|
NULL, 0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
klass->request_client_content_for_mime_type (clipboard, mime_type_table,
|
||||||
|
serial);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
free_mime_type_table (gpointer data)
|
||||||
|
{
|
||||||
|
GrdMimeTypeTable *mime_type_table = data;
|
||||||
|
|
||||||
|
g_free (mime_type_table);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
grd_clipboard_dispose (GObject *object)
|
||||||
|
{
|
||||||
|
GrdClipboard *clipboard = GRD_CLIPBOARD (object);
|
||||||
|
GrdClipboardPrivate *priv = grd_clipboard_get_instance_private (clipboard);
|
||||||
|
|
||||||
|
abort_current_read_operation (clipboard);
|
||||||
|
|
||||||
|
g_clear_pointer (&priv->client_mime_type_tables, g_hash_table_destroy);
|
||||||
|
|
||||||
|
G_OBJECT_CLASS (grd_clipboard_parent_class)->dispose (object);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
grd_clipboard_finalize (GObject *object)
|
||||||
|
{
|
||||||
|
GrdClipboard *clipboard = GRD_CLIPBOARD (object);
|
||||||
|
GrdClipboardPrivate *priv = grd_clipboard_get_instance_private (clipboard);
|
||||||
|
|
||||||
|
g_mutex_clear (&priv->pending_read_mutex);
|
||||||
|
g_cond_clear (&priv->pending_read_cond);
|
||||||
|
|
||||||
|
G_OBJECT_CLASS (grd_clipboard_parent_class)->finalize (object);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
grd_clipboard_init (GrdClipboard *clipboard)
|
||||||
|
{
|
||||||
|
GrdClipboardPrivate *priv = grd_clipboard_get_instance_private (clipboard);
|
||||||
|
|
||||||
|
priv->client_mime_type_tables = g_hash_table_new_full (NULL, NULL, NULL,
|
||||||
|
free_mime_type_table);
|
||||||
|
|
||||||
|
g_cond_init (&priv->pending_read_cond);
|
||||||
|
g_mutex_init (&priv->pending_read_mutex);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
grd_clipboard_class_init (GrdClipboardClass *klass)
|
||||||
|
{
|
||||||
|
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
||||||
|
|
||||||
|
object_class->dispose = grd_clipboard_dispose;
|
||||||
|
object_class->finalize = grd_clipboard_finalize;
|
||||||
|
}
|
||||||
68
grd-clipboard.h
Normal file
68
grd-clipboard.h
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2020-2021 Pascal Nowack
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License as
|
||||||
|
* published by the Free Software Foundation; either version 2 of the
|
||||||
|
* License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
|
||||||
|
* 02111-1307, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <glib-object.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#include "grd-mime-type.h"
|
||||||
|
#include "grd-types.h"
|
||||||
|
|
||||||
|
#define GRD_TYPE_CLIPBOARD (grd_clipboard_get_type ())
|
||||||
|
G_DECLARE_DERIVABLE_TYPE (GrdClipboard, grd_clipboard, GRD, CLIPBOARD, GObject)
|
||||||
|
|
||||||
|
struct _GrdClipboardClass
|
||||||
|
{
|
||||||
|
GObjectClass parent_class;
|
||||||
|
|
||||||
|
void (*update_client_mime_type_list) (GrdClipboard *clipboard,
|
||||||
|
GList *mime_type_list);
|
||||||
|
void (*request_client_content_for_mime_type) (GrdClipboard *clipboard,
|
||||||
|
GrdMimeTypeTable *mime_type_table,
|
||||||
|
unsigned int serial);
|
||||||
|
void (*submit_requested_server_content) (GrdClipboard *clipboard,
|
||||||
|
uint8_t *data,
|
||||||
|
uint32_t size);
|
||||||
|
};
|
||||||
|
|
||||||
|
void grd_clipboard_update_server_mime_type_list (GrdClipboard *clipboard,
|
||||||
|
GList *mime_type_tables);
|
||||||
|
|
||||||
|
void grd_clipboard_request_server_content_for_mime_type_async (GrdClipboard *clipboard,
|
||||||
|
GrdMimeType mime_type);
|
||||||
|
|
||||||
|
void grd_clipboard_initialize (GrdClipboard *clipboard,
|
||||||
|
GrdSession *session);
|
||||||
|
|
||||||
|
void grd_clipboard_maybe_enable_clipboard (GrdClipboard *clipboard);
|
||||||
|
|
||||||
|
void grd_clipboard_disable_clipboard (GrdClipboard *clipboard);
|
||||||
|
|
||||||
|
void grd_clipboard_update_client_mime_type_list (GrdClipboard *clipboard,
|
||||||
|
GList *mime_type_list);
|
||||||
|
|
||||||
|
void grd_clipboard_submit_client_content_for_mime_type (GrdClipboard *clipboard,
|
||||||
|
unsigned int serial,
|
||||||
|
const uint8_t *data,
|
||||||
|
uint32_t size);
|
||||||
|
|
||||||
|
void grd_clipboard_request_client_content_for_mime_type (GrdClipboard *clipboard,
|
||||||
|
GrdMimeType mime_type,
|
||||||
|
unsigned int serial);
|
||||||
730
grd-configuration.c
Normal file
730
grd-configuration.c
Normal file
@@ -0,0 +1,730 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2024 SUSE Software Solutions Germany GmbH
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License as
|
||||||
|
* published by the Free Software Foundation; either version 2 of the
|
||||||
|
* License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
|
||||||
|
* 02111-1307, USA.
|
||||||
|
*
|
||||||
|
* Written by:
|
||||||
|
* Joan Torres <joan.torres@suse.com>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
|
#include <gio/gio.h>
|
||||||
|
#include <glib-unix.h>
|
||||||
|
#include <glib/gstdio.h>
|
||||||
|
#include <glib.h>
|
||||||
|
#include <polkit/polkit.h>
|
||||||
|
|
||||||
|
#ifdef HAVE_RDP
|
||||||
|
#include <freerdp/freerdp.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "grd-dbus-remote-desktop.h"
|
||||||
|
#include "grd-private.h"
|
||||||
|
#include "grd-settings-system.h"
|
||||||
|
#include "grd-utils.h"
|
||||||
|
|
||||||
|
#define GRD_CONFIGURATION_TIMEOUT_S 10
|
||||||
|
#define GRD_SYSTEMD_SERVICE "gnome-remote-desktop.service"
|
||||||
|
#define GRD_SERVER_USER_CERT_SUBDIR "certificates"
|
||||||
|
#define GRD_CONFIGURE_SYSTEM_DAEMON_POLKIT_ACTION "org.gnome.remotedesktop.configure-system-daemon"
|
||||||
|
#define GRD_MAX_CERTIFICATE_FILE_SIZE_BYTES (50 * 1024)
|
||||||
|
#define GRD_MAX_PRIVATE_KEY_FILE_SIZE_BYTES (50 * 1024)
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
unsigned int *timeout_source_id;
|
||||||
|
GSourceFunc function;
|
||||||
|
gpointer data;
|
||||||
|
} TimeoutLocker;
|
||||||
|
|
||||||
|
struct _GrdConfiguration
|
||||||
|
{
|
||||||
|
GApplication parent;
|
||||||
|
|
||||||
|
PolkitAuthority *authority;
|
||||||
|
|
||||||
|
GrdSettingsSystem *settings;
|
||||||
|
|
||||||
|
GrdDBusRemoteDesktopConfigurationRdpServer *configuration_rdp_server;
|
||||||
|
unsigned int own_name_source_id;
|
||||||
|
|
||||||
|
GDBusProxy *unit_proxy;
|
||||||
|
GrdSystemdUnitActiveState unit_state;
|
||||||
|
|
||||||
|
unsigned int timeout_source_id;
|
||||||
|
unsigned int sigint_source_id;
|
||||||
|
unsigned int sigterm_source_id;
|
||||||
|
};
|
||||||
|
|
||||||
|
#define GRD_TYPE_CONFIGURATION (grd_configuration_get_type ())
|
||||||
|
G_DECLARE_FINAL_TYPE (GrdConfiguration,
|
||||||
|
grd_configuration,
|
||||||
|
GRD, CONFIGURATION,
|
||||||
|
GApplication)
|
||||||
|
|
||||||
|
G_DEFINE_TYPE (GrdConfiguration, grd_configuration, G_TYPE_APPLICATION)
|
||||||
|
|
||||||
|
static void
|
||||||
|
timeout_locker_free (TimeoutLocker *locker);
|
||||||
|
|
||||||
|
G_DEFINE_AUTOPTR_CLEANUP_FUNC (TimeoutLocker, timeout_locker_free)
|
||||||
|
|
||||||
|
#ifdef HAVE_RDP
|
||||||
|
G_DEFINE_AUTOPTR_CLEANUP_FUNC (rdpCertificate, freerdp_certificate_free)
|
||||||
|
G_DEFINE_AUTOPTR_CLEANUP_FUNC (rdpPrivateKey, freerdp_key_free)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static TimeoutLocker *
|
||||||
|
timeout_locker_new (unsigned int *timeout_source_id,
|
||||||
|
GSourceFunc function,
|
||||||
|
gpointer data)
|
||||||
|
{
|
||||||
|
TimeoutLocker *locker = g_new0 (TimeoutLocker, 1);
|
||||||
|
|
||||||
|
locker->timeout_source_id = timeout_source_id;
|
||||||
|
locker->function = function;
|
||||||
|
locker->data = data;
|
||||||
|
|
||||||
|
g_clear_handle_id (locker->timeout_source_id, g_source_remove);
|
||||||
|
|
||||||
|
return locker;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
timeout_locker_free (TimeoutLocker *locker)
|
||||||
|
{
|
||||||
|
if (*locker->timeout_source_id == 0)
|
||||||
|
{
|
||||||
|
*locker->timeout_source_id =
|
||||||
|
g_timeout_add_seconds (GRD_CONFIGURATION_TIMEOUT_S,
|
||||||
|
locker->function,
|
||||||
|
locker->data);
|
||||||
|
}
|
||||||
|
|
||||||
|
g_free (locker);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
grd_configuration_init (GrdConfiguration *app)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
on_unit_active_state_changed (GrdConfiguration *configuration)
|
||||||
|
{
|
||||||
|
if (!grd_systemd_unit_get_active_state (configuration->unit_proxy,
|
||||||
|
&configuration->unit_state,
|
||||||
|
NULL))
|
||||||
|
return;
|
||||||
|
|
||||||
|
g_object_notify (G_OBJECT (configuration->settings), "rdp-enabled");
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
watch_grd_system_unit_active_state (GrdConfiguration *configuration)
|
||||||
|
{
|
||||||
|
GDBusProxy *unit_proxy = NULL;
|
||||||
|
g_autoptr (GError) error = NULL;
|
||||||
|
|
||||||
|
if (!grd_systemd_get_unit (G_BUS_TYPE_SYSTEM,
|
||||||
|
GRD_SYSTEMD_SERVICE,
|
||||||
|
&unit_proxy,
|
||||||
|
&error))
|
||||||
|
{
|
||||||
|
g_warning ("Could not load %s: %s",
|
||||||
|
GRD_SYSTEMD_SERVICE, error->message);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
grd_systemd_unit_get_active_state (unit_proxy,
|
||||||
|
&configuration->unit_state,
|
||||||
|
NULL);
|
||||||
|
|
||||||
|
g_signal_connect_object (G_OBJECT (unit_proxy),
|
||||||
|
"g-properties-changed",
|
||||||
|
G_CALLBACK (on_unit_active_state_changed),
|
||||||
|
configuration,
|
||||||
|
G_CONNECT_SWAPPED);
|
||||||
|
|
||||||
|
configuration->unit_proxy = unit_proxy;
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
transform_enabled (GBinding *binding,
|
||||||
|
const GValue *from_value,
|
||||||
|
GValue *to_value,
|
||||||
|
gpointer user_data)
|
||||||
|
{
|
||||||
|
GrdConfiguration *configuration = user_data;
|
||||||
|
gboolean enabled;
|
||||||
|
GrdSystemdUnitActiveState active_state;
|
||||||
|
|
||||||
|
enabled = g_value_get_boolean (from_value);
|
||||||
|
active_state = configuration->unit_state;
|
||||||
|
|
||||||
|
g_value_set_boolean (to_value,
|
||||||
|
enabled &&
|
||||||
|
(active_state == GRD_SYSTEMD_UNIT_ACTIVE_STATE_ACTIVE ||
|
||||||
|
active_state == GRD_SYSTEMD_UNIT_ACTIVE_STATE_ACTIVATING));
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
on_handle_enable (GrdDBusRemoteDesktopConfigurationRdpServer *configuration_rdp_server,
|
||||||
|
GDBusMethodInvocation *invocation,
|
||||||
|
GrdConfiguration *configuration)
|
||||||
|
{
|
||||||
|
g_autoptr (GError) error = NULL;
|
||||||
|
|
||||||
|
if (!grd_toggle_systemd_unit (GRD_RUNTIME_MODE_SYSTEM, TRUE, &error))
|
||||||
|
{
|
||||||
|
g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR,
|
||||||
|
G_DBUS_ERROR_FAILED,
|
||||||
|
"Failed enabling %s: %s",
|
||||||
|
GRD_SYSTEMD_SERVICE,
|
||||||
|
error->message);
|
||||||
|
return G_DBUS_METHOD_INVOCATION_HANDLED;
|
||||||
|
}
|
||||||
|
|
||||||
|
g_object_set (G_OBJECT (configuration->settings), "rdp-enabled", TRUE, NULL);
|
||||||
|
|
||||||
|
grd_dbus_remote_desktop_configuration_rdp_server_complete_enable (
|
||||||
|
configuration_rdp_server,
|
||||||
|
invocation);
|
||||||
|
|
||||||
|
return G_DBUS_METHOD_INVOCATION_HANDLED;
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
on_handle_disable (GrdDBusRemoteDesktopConfigurationRdpServer *configuration_rdp_server,
|
||||||
|
GDBusMethodInvocation *invocation,
|
||||||
|
GrdConfiguration *configuration)
|
||||||
|
{
|
||||||
|
g_autoptr (GError) error = NULL;
|
||||||
|
|
||||||
|
if (!grd_toggle_systemd_unit (GRD_RUNTIME_MODE_SYSTEM, FALSE, &error))
|
||||||
|
{
|
||||||
|
g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR,
|
||||||
|
G_DBUS_ERROR_FAILED,
|
||||||
|
"Failed disabling %s: %s",
|
||||||
|
GRD_SYSTEMD_SERVICE,
|
||||||
|
error->message);
|
||||||
|
return G_DBUS_METHOD_INVOCATION_HANDLED;
|
||||||
|
}
|
||||||
|
|
||||||
|
g_object_set (G_OBJECT (configuration->settings), "rdp-enabled", FALSE, NULL);
|
||||||
|
|
||||||
|
grd_dbus_remote_desktop_configuration_rdp_server_complete_disable (
|
||||||
|
configuration_rdp_server,
|
||||||
|
invocation);
|
||||||
|
|
||||||
|
return G_DBUS_METHOD_INVOCATION_HANDLED;
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
on_handle_get_credentials (GrdDBusRemoteDesktopConfigurationRdpServer *configuration_rdp_server,
|
||||||
|
GDBusMethodInvocation *invocation,
|
||||||
|
GrdConfiguration *configuration)
|
||||||
|
{
|
||||||
|
g_autofree char *username = NULL;
|
||||||
|
g_autofree char *password = NULL;
|
||||||
|
g_autoptr (GError) error = NULL;
|
||||||
|
GVariantBuilder credentials;
|
||||||
|
|
||||||
|
g_variant_builder_init (&credentials, G_VARIANT_TYPE ("a{sv}"));
|
||||||
|
|
||||||
|
grd_settings_get_rdp_credentials (GRD_SETTINGS (configuration->settings),
|
||||||
|
&username, &password,
|
||||||
|
&error);
|
||||||
|
if (error)
|
||||||
|
g_warning ("[Configuration] Failed to get credentials: %s", error->message);
|
||||||
|
|
||||||
|
if (!username)
|
||||||
|
username = g_strdup ("");
|
||||||
|
|
||||||
|
if (!password)
|
||||||
|
password = g_strdup ("");
|
||||||
|
|
||||||
|
g_variant_builder_add (&credentials, "{sv}", "username",
|
||||||
|
g_variant_new_string (username));
|
||||||
|
g_variant_builder_add (&credentials, "{sv}", "password",
|
||||||
|
g_variant_new_string (password));
|
||||||
|
|
||||||
|
grd_dbus_remote_desktop_configuration_rdp_server_complete_get_credentials (
|
||||||
|
configuration_rdp_server,
|
||||||
|
invocation,
|
||||||
|
g_variant_builder_end (&credentials));
|
||||||
|
|
||||||
|
return G_DBUS_METHOD_INVOCATION_HANDLED;
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
on_handle_set_credentials (GrdDBusRemoteDesktopConfigurationRdpServer *configuration_rdp_server,
|
||||||
|
GDBusMethodInvocation *invocation,
|
||||||
|
GVariant *credentials,
|
||||||
|
GrdConfiguration *configuration)
|
||||||
|
{
|
||||||
|
g_autofree char *old_username = NULL;
|
||||||
|
g_autofree char *old_password = NULL;
|
||||||
|
g_autofree char *username = NULL;
|
||||||
|
g_autofree char *password = NULL;
|
||||||
|
g_autoptr (GError) error = NULL;
|
||||||
|
|
||||||
|
g_variant_lookup (credentials, "username", "s", &username);
|
||||||
|
g_variant_lookup (credentials, "password", "s", &password);
|
||||||
|
|
||||||
|
if (!username && !password)
|
||||||
|
{
|
||||||
|
g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR,
|
||||||
|
G_DBUS_ERROR_INVALID_ARGS,
|
||||||
|
"Username or password expected "
|
||||||
|
"in credentials");
|
||||||
|
return G_DBUS_METHOD_INVOCATION_HANDLED;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!username || !password)
|
||||||
|
{
|
||||||
|
grd_settings_get_rdp_credentials (GRD_SETTINGS (configuration->settings),
|
||||||
|
&old_username, &old_password,
|
||||||
|
NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!username)
|
||||||
|
username = g_steal_pointer (&old_username);
|
||||||
|
|
||||||
|
if (!password)
|
||||||
|
password = g_steal_pointer (&old_password);
|
||||||
|
|
||||||
|
if (!grd_settings_set_rdp_credentials (GRD_SETTINGS (configuration->settings),
|
||||||
|
username, password,
|
||||||
|
&error))
|
||||||
|
{
|
||||||
|
g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR,
|
||||||
|
G_DBUS_ERROR_FAILED,
|
||||||
|
"Failed to set credentials: %s",
|
||||||
|
error->message);
|
||||||
|
return G_DBUS_METHOD_INVOCATION_HANDLED;
|
||||||
|
}
|
||||||
|
|
||||||
|
grd_dbus_remote_desktop_configuration_rdp_server_complete_set_credentials (
|
||||||
|
configuration_rdp_server,
|
||||||
|
invocation);
|
||||||
|
|
||||||
|
return G_DBUS_METHOD_INVOCATION_HANDLED;
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
on_handle_import_certificate (GrdDBusRemoteDesktopConfigurationRdpServer *configuration_rdp_server,
|
||||||
|
GDBusMethodInvocation *invocation,
|
||||||
|
GUnixFDList *fd_list,
|
||||||
|
GVariant *certificate,
|
||||||
|
GVariant *private_key,
|
||||||
|
GrdConfiguration *configuration)
|
||||||
|
{
|
||||||
|
g_autoptr (rdpCertificate) rdp_certificate = NULL;
|
||||||
|
g_autoptr (rdpPrivateKey) rdp_private_key = NULL;
|
||||||
|
g_autofree char *certificate_filename = NULL;
|
||||||
|
g_autofree char *key_filename = NULL;
|
||||||
|
g_autoptr (GError) error = NULL;
|
||||||
|
g_autofd int certificate_fd = -1;
|
||||||
|
g_autofd int key_fd = -1;
|
||||||
|
GFileTest fd_test_results;
|
||||||
|
int certificate_fd_index = -1;
|
||||||
|
int key_fd_index = -1;
|
||||||
|
gboolean success;
|
||||||
|
|
||||||
|
g_variant_get (certificate, "(sh)", &certificate_filename,
|
||||||
|
&certificate_fd_index);
|
||||||
|
g_variant_get (private_key, "(sh)", &key_filename,
|
||||||
|
&key_fd_index);
|
||||||
|
|
||||||
|
if (!G_IS_UNIX_FD_LIST (fd_list))
|
||||||
|
{
|
||||||
|
g_set_error (&error, G_IO_ERROR, G_IO_ERROR_FAILED, "Failed to acquire "
|
||||||
|
"file descriptor for certificate and private key: "
|
||||||
|
"The sender supplied an invalid fd list");
|
||||||
|
g_dbus_method_invocation_return_gerror (invocation, error);
|
||||||
|
return G_DBUS_METHOD_INVOCATION_HANDLED;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (certificate_fd_index < 0 ||
|
||||||
|
certificate_fd_index >= g_unix_fd_list_get_length (fd_list))
|
||||||
|
{
|
||||||
|
g_set_error (&error, G_IO_ERROR, G_IO_ERROR_FAILED, "Failed to acquire "
|
||||||
|
"file descriptor for certificate: "
|
||||||
|
"The sender supplied an invalid fd index");
|
||||||
|
g_dbus_method_invocation_return_gerror (invocation, error);
|
||||||
|
return G_DBUS_METHOD_INVOCATION_HANDLED;
|
||||||
|
}
|
||||||
|
if (key_fd_index < 0 ||
|
||||||
|
key_fd_index >= g_unix_fd_list_get_length (fd_list))
|
||||||
|
{
|
||||||
|
g_set_error (&error, G_IO_ERROR, G_IO_ERROR_FAILED, "Failed to acquire "
|
||||||
|
"file descriptor for private key: "
|
||||||
|
"The sender supplied an invalid fd index");
|
||||||
|
g_dbus_method_invocation_return_gerror (invocation, error);
|
||||||
|
return G_DBUS_METHOD_INVOCATION_HANDLED;
|
||||||
|
}
|
||||||
|
|
||||||
|
certificate_fd = g_unix_fd_list_get (fd_list, certificate_fd_index, &error);
|
||||||
|
if (certificate_fd == -1)
|
||||||
|
{
|
||||||
|
g_dbus_method_invocation_return_gerror (invocation, error);
|
||||||
|
return G_DBUS_METHOD_INVOCATION_HANDLED;
|
||||||
|
}
|
||||||
|
|
||||||
|
success = grd_test_fd (certificate_fd, GRD_MAX_CERTIFICATE_FILE_SIZE_BYTES,
|
||||||
|
&fd_test_results, &error);
|
||||||
|
if (!success)
|
||||||
|
{
|
||||||
|
g_prefix_error (&error, "Could not inspect certificate file descriptor");
|
||||||
|
g_dbus_method_invocation_return_gerror (invocation, error);
|
||||||
|
return G_DBUS_METHOD_INVOCATION_HANDLED;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(fd_test_results & G_FILE_TEST_IS_REGULAR))
|
||||||
|
{
|
||||||
|
g_dbus_method_invocation_return_error (invocation,
|
||||||
|
G_IO_ERROR,
|
||||||
|
G_IO_ERROR_NOT_REGULAR_FILE,
|
||||||
|
"Invalid certificate file");
|
||||||
|
return G_DBUS_METHOD_INVOCATION_HANDLED;
|
||||||
|
}
|
||||||
|
|
||||||
|
key_fd = g_unix_fd_list_get (fd_list, key_fd_index, &error);
|
||||||
|
if (key_fd == -1)
|
||||||
|
{
|
||||||
|
g_dbus_method_invocation_return_gerror (invocation, error);
|
||||||
|
return G_DBUS_METHOD_INVOCATION_HANDLED;
|
||||||
|
}
|
||||||
|
|
||||||
|
success = grd_test_fd (key_fd, GRD_MAX_PRIVATE_KEY_FILE_SIZE_BYTES,
|
||||||
|
&fd_test_results, &error);
|
||||||
|
if (!success)
|
||||||
|
{
|
||||||
|
g_prefix_error (&error, "Could not inspect private key file descriptor");
|
||||||
|
g_dbus_method_invocation_return_gerror (invocation, error);
|
||||||
|
return G_DBUS_METHOD_INVOCATION_HANDLED;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(fd_test_results & G_FILE_TEST_IS_REGULAR))
|
||||||
|
{
|
||||||
|
g_dbus_method_invocation_return_error (invocation,
|
||||||
|
G_IO_ERROR,
|
||||||
|
G_IO_ERROR_NOT_REGULAR_FILE,
|
||||||
|
"Invalid private key file");
|
||||||
|
return G_DBUS_METHOD_INVOCATION_HANDLED;
|
||||||
|
}
|
||||||
|
|
||||||
|
grd_rewrite_path_to_user_data_dir (&certificate_filename,
|
||||||
|
GRD_SERVER_USER_CERT_SUBDIR,
|
||||||
|
"rdp-tls.crt");
|
||||||
|
if (!grd_write_fd_to_file (certificate_fd, certificate_filename,
|
||||||
|
NULL, &error))
|
||||||
|
{
|
||||||
|
g_dbus_method_invocation_return_gerror (invocation, error);
|
||||||
|
return G_DBUS_METHOD_INVOCATION_HANDLED;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (certificate_filename)
|
||||||
|
rdp_certificate = freerdp_certificate_new_from_file (certificate_filename);
|
||||||
|
|
||||||
|
if (!rdp_certificate)
|
||||||
|
{
|
||||||
|
g_dbus_method_invocation_return_error (invocation,
|
||||||
|
G_IO_ERROR,
|
||||||
|
G_IO_ERROR_INVALID_DATA,
|
||||||
|
"Invalid certificate");
|
||||||
|
return G_DBUS_METHOD_INVOCATION_HANDLED;
|
||||||
|
}
|
||||||
|
|
||||||
|
grd_rewrite_path_to_user_data_dir (&key_filename,
|
||||||
|
GRD_SERVER_USER_CERT_SUBDIR,
|
||||||
|
"rdp-tls.key");
|
||||||
|
if (!grd_write_fd_to_file (key_fd, key_filename, NULL, &error))
|
||||||
|
{
|
||||||
|
g_dbus_method_invocation_return_gerror (invocation, error);
|
||||||
|
return G_DBUS_METHOD_INVOCATION_HANDLED;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (key_filename)
|
||||||
|
rdp_private_key = freerdp_key_new_from_file (key_filename);
|
||||||
|
|
||||||
|
if (!rdp_private_key)
|
||||||
|
{
|
||||||
|
g_dbus_method_invocation_return_error (invocation,
|
||||||
|
G_IO_ERROR,
|
||||||
|
G_IO_ERROR_INVALID_DATA,
|
||||||
|
"Invalid private key");
|
||||||
|
return G_DBUS_METHOD_INVOCATION_HANDLED;
|
||||||
|
}
|
||||||
|
|
||||||
|
g_object_set (configuration->settings,
|
||||||
|
"rdp-server-cert-path", certificate_filename,
|
||||||
|
"rdp-server-key-path", key_filename,
|
||||||
|
NULL);
|
||||||
|
|
||||||
|
grd_dbus_remote_desktop_configuration_rdp_server_complete_import_certificate (
|
||||||
|
configuration_rdp_server,
|
||||||
|
invocation,
|
||||||
|
fd_list);
|
||||||
|
|
||||||
|
return G_DBUS_METHOD_INVOCATION_HANDLED;
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
terminate (gpointer user_data)
|
||||||
|
{
|
||||||
|
GrdConfiguration *configuration = user_data;
|
||||||
|
|
||||||
|
g_clear_handle_id (&configuration->timeout_source_id, g_source_remove);
|
||||||
|
g_clear_handle_id (&configuration->sigint_source_id, g_source_remove);
|
||||||
|
g_clear_handle_id (&configuration->sigterm_source_id, g_source_remove);
|
||||||
|
|
||||||
|
g_application_release (G_APPLICATION (configuration));
|
||||||
|
|
||||||
|
return G_SOURCE_REMOVE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
ensure_polkit_authority (GrdConfiguration *configuration,
|
||||||
|
GError **error)
|
||||||
|
{
|
||||||
|
if (configuration->authority)
|
||||||
|
return TRUE;
|
||||||
|
|
||||||
|
configuration->authority = polkit_authority_get_sync (NULL, error);
|
||||||
|
|
||||||
|
return configuration->authority != NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
on_authorize_method (GrdDBusRemoteDesktopConfigurationRdpServer *configuration_rdp_server,
|
||||||
|
GDBusMethodInvocation *invocation,
|
||||||
|
GrdConfiguration *configuration)
|
||||||
|
{
|
||||||
|
g_autoptr (PolkitAuthorizationResult) result = NULL;
|
||||||
|
g_autoptr (PolkitSubject) subject = NULL;
|
||||||
|
g_autoptr (TimeoutLocker) locker = NULL;
|
||||||
|
PolkitCheckAuthorizationFlags flags;
|
||||||
|
g_autoptr (GError) error = NULL;
|
||||||
|
const char *sender = NULL;
|
||||||
|
const char *action = NULL;
|
||||||
|
|
||||||
|
locker = timeout_locker_new (&configuration->timeout_source_id, terminate, configuration);
|
||||||
|
|
||||||
|
if (!ensure_polkit_authority (configuration, &error))
|
||||||
|
{
|
||||||
|
g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR,
|
||||||
|
G_DBUS_ERROR_ACCESS_DENIED,
|
||||||
|
"Couldn't get polkit authority: %s",
|
||||||
|
error->message);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
sender = g_dbus_method_invocation_get_sender (invocation);
|
||||||
|
subject = polkit_system_bus_name_new (sender);
|
||||||
|
action = GRD_CONFIGURE_SYSTEM_DAEMON_POLKIT_ACTION;
|
||||||
|
flags = POLKIT_CHECK_AUTHORIZATION_FLAGS_ALLOW_USER_INTERACTION;
|
||||||
|
result = polkit_authority_check_authorization_sync (configuration->authority,
|
||||||
|
subject, action,
|
||||||
|
NULL, flags, NULL,
|
||||||
|
&error);
|
||||||
|
if (!result)
|
||||||
|
{
|
||||||
|
g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR,
|
||||||
|
G_DBUS_ERROR_FAILED,
|
||||||
|
"Failed to check authorization: %s",
|
||||||
|
error->message);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!polkit_authorization_result_get_is_authorized (result))
|
||||||
|
{
|
||||||
|
g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR,
|
||||||
|
G_DBUS_ERROR_ACCESS_DENIED,
|
||||||
|
"Not authorized for action %s",
|
||||||
|
action);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
on_bus_acquired (GDBusConnection *connection,
|
||||||
|
const char *name,
|
||||||
|
gpointer user_data)
|
||||||
|
{
|
||||||
|
GrdConfiguration *configuration = user_data;
|
||||||
|
|
||||||
|
g_debug ("[Configuration] Now on system bus");
|
||||||
|
|
||||||
|
g_dbus_interface_skeleton_export (
|
||||||
|
G_DBUS_INTERFACE_SKELETON (configuration->configuration_rdp_server),
|
||||||
|
connection,
|
||||||
|
REMOTE_DESKTOP_CONFIGURATION_OBJECT_PATH,
|
||||||
|
NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
on_name_acquired (GDBusConnection *connection,
|
||||||
|
const char *name,
|
||||||
|
gpointer user_data)
|
||||||
|
{
|
||||||
|
g_debug ("[Configuration] Owned %s name", name);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
on_name_lost (GDBusConnection *connection,
|
||||||
|
const char *name,
|
||||||
|
gpointer user_data)
|
||||||
|
{
|
||||||
|
g_debug ("[Configuration] Lost owned %s name", name);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
register_signals (GrdConfiguration *configuration)
|
||||||
|
{
|
||||||
|
configuration->timeout_source_id =
|
||||||
|
g_timeout_add_seconds (GRD_CONFIGURATION_TIMEOUT_S,
|
||||||
|
terminate,
|
||||||
|
configuration);
|
||||||
|
configuration->sigint_source_id = g_unix_signal_add (SIGINT,
|
||||||
|
terminate,
|
||||||
|
configuration);
|
||||||
|
configuration->sigterm_source_id = g_unix_signal_add (SIGTERM,
|
||||||
|
terminate,
|
||||||
|
configuration);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
grd_configuration_startup (GApplication *application)
|
||||||
|
{
|
||||||
|
GrdConfiguration *configuration = GRD_CONFIGURATION (application);
|
||||||
|
|
||||||
|
configuration->settings = grd_settings_system_new ();
|
||||||
|
grd_settings_system_use_local_state (configuration->settings);
|
||||||
|
|
||||||
|
configuration->configuration_rdp_server =
|
||||||
|
grd_dbus_remote_desktop_configuration_rdp_server_skeleton_new ();
|
||||||
|
|
||||||
|
watch_grd_system_unit_active_state (configuration);
|
||||||
|
|
||||||
|
g_object_bind_property_full (configuration->settings, "rdp-enabled",
|
||||||
|
configuration->configuration_rdp_server, "enabled",
|
||||||
|
G_BINDING_SYNC_CREATE,
|
||||||
|
transform_enabled,
|
||||||
|
NULL,
|
||||||
|
configuration,
|
||||||
|
NULL);
|
||||||
|
g_object_bind_property (configuration->settings, "rdp-port",
|
||||||
|
configuration->configuration_rdp_server, "port",
|
||||||
|
G_BINDING_SYNC_CREATE);
|
||||||
|
g_object_bind_property (configuration->settings, "rdp-server-cert-path",
|
||||||
|
configuration->configuration_rdp_server, "tls-cert",
|
||||||
|
G_BINDING_SYNC_CREATE);
|
||||||
|
g_object_bind_property (configuration->settings, "rdp-server-fingerprint",
|
||||||
|
configuration->configuration_rdp_server, "tls-fingerprint",
|
||||||
|
G_BINDING_SYNC_CREATE);
|
||||||
|
g_object_bind_property (configuration->settings, "rdp-server-key-path",
|
||||||
|
configuration->configuration_rdp_server, "tls-key",
|
||||||
|
G_BINDING_SYNC_CREATE);
|
||||||
|
g_object_bind_property (configuration->settings, "rdp-auth-methods",
|
||||||
|
configuration->configuration_rdp_server, "auth-methods",
|
||||||
|
G_BINDING_SYNC_CREATE);
|
||||||
|
g_signal_connect_object (configuration->configuration_rdp_server, "handle-enable",
|
||||||
|
G_CALLBACK (on_handle_enable),
|
||||||
|
configuration, 0);
|
||||||
|
g_signal_connect_object (configuration->configuration_rdp_server, "handle-disable",
|
||||||
|
G_CALLBACK (on_handle_disable),
|
||||||
|
configuration, 0);
|
||||||
|
g_signal_connect_object (configuration->configuration_rdp_server, "handle-get-credentials",
|
||||||
|
G_CALLBACK (on_handle_get_credentials),
|
||||||
|
configuration, 0);
|
||||||
|
g_signal_connect_object (configuration->configuration_rdp_server, "handle-set-credentials",
|
||||||
|
G_CALLBACK (on_handle_set_credentials),
|
||||||
|
configuration, 0);
|
||||||
|
g_signal_connect_object (configuration->configuration_rdp_server, "handle-import-certificate",
|
||||||
|
G_CALLBACK (on_handle_import_certificate),
|
||||||
|
configuration, 0);
|
||||||
|
g_signal_connect_object (configuration->configuration_rdp_server, "g-authorize-method",
|
||||||
|
G_CALLBACK (on_authorize_method),
|
||||||
|
configuration, 0);
|
||||||
|
|
||||||
|
configuration->own_name_source_id =
|
||||||
|
g_bus_own_name (G_BUS_TYPE_SYSTEM,
|
||||||
|
REMOTE_DESKTOP_CONFIGURATION_BUS_NAME,
|
||||||
|
G_BUS_NAME_OWNER_FLAGS_NONE,
|
||||||
|
on_bus_acquired,
|
||||||
|
on_name_acquired,
|
||||||
|
on_name_lost,
|
||||||
|
configuration, NULL);
|
||||||
|
|
||||||
|
register_signals (configuration);
|
||||||
|
|
||||||
|
g_application_hold (application);
|
||||||
|
|
||||||
|
G_APPLICATION_CLASS (grd_configuration_parent_class)->startup (application);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
grd_configuration_shutdown (GApplication *application)
|
||||||
|
{
|
||||||
|
GrdConfiguration *configuration = GRD_CONFIGURATION (application);
|
||||||
|
|
||||||
|
g_clear_object (&configuration->authority);
|
||||||
|
|
||||||
|
g_clear_object (&configuration->settings);
|
||||||
|
g_clear_object (&configuration->unit_proxy);
|
||||||
|
|
||||||
|
g_dbus_interface_skeleton_unexport (
|
||||||
|
G_DBUS_INTERFACE_SKELETON (configuration->configuration_rdp_server));
|
||||||
|
g_clear_object (&configuration->configuration_rdp_server);
|
||||||
|
|
||||||
|
g_clear_handle_id (&configuration->own_name_source_id, g_bus_unown_name);
|
||||||
|
|
||||||
|
g_clear_handle_id (&configuration->timeout_source_id, g_source_remove);
|
||||||
|
g_clear_handle_id (&configuration->sigint_source_id, g_source_remove);
|
||||||
|
g_clear_handle_id (&configuration->sigterm_source_id, g_source_remove);
|
||||||
|
|
||||||
|
G_APPLICATION_CLASS (grd_configuration_parent_class)->shutdown (application);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
grd_configuration_class_init (GrdConfigurationClass *klass)
|
||||||
|
{
|
||||||
|
GApplicationClass *g_application_class = G_APPLICATION_CLASS (klass);
|
||||||
|
|
||||||
|
g_application_class->startup = grd_configuration_startup;
|
||||||
|
g_application_class->shutdown = grd_configuration_shutdown;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
main (int argc,
|
||||||
|
char **argv)
|
||||||
|
{
|
||||||
|
g_autoptr (GApplication) app = NULL;
|
||||||
|
|
||||||
|
app = g_object_new (grd_configuration_get_type (),
|
||||||
|
"application-id", REMOTE_DESKTOP_CONFIGURATION_BUS_NAME,
|
||||||
|
"flags", G_APPLICATION_IS_SERVICE,
|
||||||
|
NULL);
|
||||||
|
|
||||||
|
return g_application_run (app, argc, argv);
|
||||||
|
}
|
||||||
200
grd-context.c
Normal file
200
grd-context.c
Normal file
@@ -0,0 +1,200 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2015 Red Hat Inc.
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License as
|
||||||
|
* published by the Free Software Foundation; either version 2 of the
|
||||||
|
* License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
|
||||||
|
* 02111-1307, USA.
|
||||||
|
*
|
||||||
|
* Written by:
|
||||||
|
* Jonas Ådahl <jadahl@gmail.com>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
|
#include "grd-context.h"
|
||||||
|
|
||||||
|
#include "grd-egl-thread.h"
|
||||||
|
#include "grd-settings-handover.h"
|
||||||
|
#include "grd-settings-headless.h"
|
||||||
|
#include "grd-settings-system.h"
|
||||||
|
#include "grd-settings-user.h"
|
||||||
|
|
||||||
|
#include "grd-dbus-mutter-remote-desktop.h"
|
||||||
|
#include "grd-dbus-mutter-screen-cast.h"
|
||||||
|
|
||||||
|
struct _GrdContext
|
||||||
|
{
|
||||||
|
GObject parent;
|
||||||
|
|
||||||
|
GrdDBusMutterRemoteDesktop *mutter_remote_desktop_proxy;
|
||||||
|
GrdDBusMutterScreenCast *mutter_screen_cast_proxy;
|
||||||
|
|
||||||
|
GrdEglThread *egl_thread;
|
||||||
|
|
||||||
|
GrdSettings *settings;
|
||||||
|
|
||||||
|
GrdRuntimeMode runtime_mode;
|
||||||
|
|
||||||
|
GrdDBusRemoteDesktopRdpServer *rdp_server_iface;
|
||||||
|
GrdDBusRemoteDesktopVncServer *vnc_server_iface;
|
||||||
|
};
|
||||||
|
|
||||||
|
G_DEFINE_TYPE (GrdContext, grd_context, G_TYPE_OBJECT)
|
||||||
|
|
||||||
|
GrdDBusMutterRemoteDesktop *
|
||||||
|
grd_context_get_mutter_remote_desktop_proxy (GrdContext *context)
|
||||||
|
{
|
||||||
|
return context->mutter_remote_desktop_proxy;
|
||||||
|
}
|
||||||
|
|
||||||
|
GrdDBusMutterScreenCast *
|
||||||
|
grd_context_get_mutter_screen_cast_proxy (GrdContext *context)
|
||||||
|
{
|
||||||
|
return context->mutter_screen_cast_proxy;
|
||||||
|
}
|
||||||
|
|
||||||
|
GrdDBusRemoteDesktopRdpServer *
|
||||||
|
grd_context_get_rdp_server_interface (GrdContext *context)
|
||||||
|
{
|
||||||
|
return context->rdp_server_iface;
|
||||||
|
}
|
||||||
|
|
||||||
|
GrdDBusRemoteDesktopVncServer *
|
||||||
|
grd_context_get_vnc_server_interface (GrdContext *context)
|
||||||
|
{
|
||||||
|
return context->vnc_server_iface;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
grd_context_set_mutter_remote_desktop_proxy (GrdContext *context,
|
||||||
|
GrdDBusMutterRemoteDesktop *proxy)
|
||||||
|
{
|
||||||
|
g_clear_object (&context->mutter_remote_desktop_proxy);
|
||||||
|
context->mutter_remote_desktop_proxy = proxy;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
grd_context_set_mutter_screen_cast_proxy (GrdContext *context,
|
||||||
|
GrdDBusMutterScreenCast *proxy)
|
||||||
|
{
|
||||||
|
g_clear_object (&context->mutter_screen_cast_proxy);
|
||||||
|
context->mutter_screen_cast_proxy = proxy;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
grd_context_set_rdp_server_interface (
|
||||||
|
GrdContext *context,
|
||||||
|
GrdDBusRemoteDesktopRdpServer *rdp_server_iface)
|
||||||
|
{
|
||||||
|
g_clear_object (&context->rdp_server_iface);
|
||||||
|
context->rdp_server_iface = rdp_server_iface;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
grd_context_set_vnc_server_interface (
|
||||||
|
GrdContext *context,
|
||||||
|
GrdDBusRemoteDesktopVncServer *vnc_server_iface)
|
||||||
|
{
|
||||||
|
g_clear_object (&context->vnc_server_iface);
|
||||||
|
context->vnc_server_iface = vnc_server_iface;
|
||||||
|
}
|
||||||
|
|
||||||
|
GrdSettings *
|
||||||
|
grd_context_get_settings (GrdContext *context)
|
||||||
|
{
|
||||||
|
return context->settings;
|
||||||
|
}
|
||||||
|
|
||||||
|
GrdEglThread *
|
||||||
|
grd_context_get_egl_thread (GrdContext *context)
|
||||||
|
{
|
||||||
|
return context->egl_thread;
|
||||||
|
}
|
||||||
|
|
||||||
|
GrdRuntimeMode
|
||||||
|
grd_context_get_runtime_mode (GrdContext *context)
|
||||||
|
{
|
||||||
|
return context->runtime_mode;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
grd_context_notify_daemon_ready (GrdContext *context)
|
||||||
|
{
|
||||||
|
g_autoptr (GError) error = NULL;
|
||||||
|
|
||||||
|
if (context->egl_thread ||
|
||||||
|
context->runtime_mode == GRD_RUNTIME_MODE_SYSTEM)
|
||||||
|
return;
|
||||||
|
|
||||||
|
context->egl_thread = grd_egl_thread_new (&error);
|
||||||
|
if (!context->egl_thread)
|
||||||
|
g_debug ("Failed to create EGL thread: %s", error->message);
|
||||||
|
}
|
||||||
|
|
||||||
|
GrdContext *
|
||||||
|
grd_context_new (GrdRuntimeMode runtime_mode,
|
||||||
|
GError **error)
|
||||||
|
{
|
||||||
|
g_autoptr (GrdContext) context = NULL;
|
||||||
|
|
||||||
|
context = g_object_new (GRD_TYPE_CONTEXT, NULL);
|
||||||
|
context->runtime_mode = runtime_mode;
|
||||||
|
|
||||||
|
switch (runtime_mode)
|
||||||
|
{
|
||||||
|
case GRD_RUNTIME_MODE_SCREEN_SHARE:
|
||||||
|
context->settings = GRD_SETTINGS (grd_settings_user_new ());
|
||||||
|
break;
|
||||||
|
case GRD_RUNTIME_MODE_HEADLESS:
|
||||||
|
context->settings = GRD_SETTINGS (grd_settings_headless_new ());
|
||||||
|
break;
|
||||||
|
case GRD_RUNTIME_MODE_SYSTEM:
|
||||||
|
context->settings = GRD_SETTINGS (grd_settings_system_new ());
|
||||||
|
break;
|
||||||
|
case GRD_RUNTIME_MODE_HANDOVER:
|
||||||
|
context->settings = GRD_SETTINGS (grd_settings_handover_new ());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!context->settings)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
return g_steal_pointer (&context);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
grd_context_finalize (GObject *object)
|
||||||
|
{
|
||||||
|
GrdContext *context = GRD_CONTEXT (object);
|
||||||
|
|
||||||
|
g_clear_object (&context->mutter_remote_desktop_proxy);
|
||||||
|
g_clear_object (&context->mutter_screen_cast_proxy);
|
||||||
|
g_clear_pointer (&context->egl_thread, grd_egl_thread_free);
|
||||||
|
g_clear_object (&context->settings);
|
||||||
|
|
||||||
|
G_OBJECT_CLASS (grd_context_parent_class)->finalize (object);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
grd_context_init (GrdContext *context)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
grd_context_class_init (GrdContextClass *klass)
|
||||||
|
{
|
||||||
|
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
||||||
|
|
||||||
|
object_class->finalize = grd_context_finalize;
|
||||||
|
}
|
||||||
65
grd-context.h
Normal file
65
grd-context.h
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2015 Red Hat Inc.
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License as
|
||||||
|
* published by the Free Software Foundation; either version 2 of the
|
||||||
|
* License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
|
||||||
|
* 02111-1307, USA.
|
||||||
|
*
|
||||||
|
* Written by:
|
||||||
|
* Jonas Ådahl <jadahl@gmail.com>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <glib-object.h>
|
||||||
|
|
||||||
|
#include "grd-dbus-mutter-remote-desktop.h"
|
||||||
|
#include "grd-dbus-mutter-screen-cast.h"
|
||||||
|
#include "grd-dbus-remote-desktop.h"
|
||||||
|
#include "grd-settings.h"
|
||||||
|
#include "grd-types.h"
|
||||||
|
|
||||||
|
#define GRD_TYPE_CONTEXT (grd_context_get_type ())
|
||||||
|
G_DECLARE_FINAL_TYPE (GrdContext, grd_context, GRD, CONTEXT, GObject)
|
||||||
|
|
||||||
|
GrdContext * grd_context_new (GrdRuntimeMode runtime_mode,
|
||||||
|
GError **error);
|
||||||
|
|
||||||
|
GrdDBusMutterRemoteDesktop * grd_context_get_mutter_remote_desktop_proxy (GrdContext *context);
|
||||||
|
|
||||||
|
GrdDBusMutterScreenCast * grd_context_get_mutter_screen_cast_proxy (GrdContext *context);
|
||||||
|
|
||||||
|
GrdDBusRemoteDesktopRdpServer * grd_context_get_rdp_server_interface (GrdContext *context);
|
||||||
|
|
||||||
|
GrdDBusRemoteDesktopVncServer * grd_context_get_vnc_server_interface (GrdContext *context);
|
||||||
|
|
||||||
|
void grd_context_set_mutter_remote_desktop_proxy (GrdContext *context,
|
||||||
|
GrdDBusMutterRemoteDesktop *proxy);
|
||||||
|
|
||||||
|
void grd_context_set_mutter_screen_cast_proxy (GrdContext *context,
|
||||||
|
GrdDBusMutterScreenCast *proxy);
|
||||||
|
|
||||||
|
void grd_context_set_rdp_server_interface (GrdContext *context,
|
||||||
|
GrdDBusRemoteDesktopRdpServer *rdp_server_iface);
|
||||||
|
|
||||||
|
void grd_context_set_vnc_server_interface (GrdContext *context,
|
||||||
|
GrdDBusRemoteDesktopVncServer *vnc_server_iface);
|
||||||
|
|
||||||
|
GrdSettings * grd_context_get_settings (GrdContext *context);
|
||||||
|
|
||||||
|
GrdEglThread * grd_context_get_egl_thread (GrdContext *context);
|
||||||
|
|
||||||
|
GrdRuntimeMode grd_context_get_runtime_mode (GrdContext *context);
|
||||||
|
|
||||||
|
void grd_context_notify_daemon_ready (GrdContext *context);
|
||||||
104
grd-control.c
Normal file
104
grd-control.c
Normal file
@@ -0,0 +1,104 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2015 Red Hat Inc.
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License as
|
||||||
|
* published by the Free Software Foundation; either version 2 of the
|
||||||
|
* License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
|
||||||
|
* 02111-1307, USA.
|
||||||
|
*
|
||||||
|
* Written by:
|
||||||
|
* Jonas Ådahl <jadahl@gmail.com>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
|
#include <gio/gio.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "grd-private.h"
|
||||||
|
|
||||||
|
int
|
||||||
|
main (int argc, char **argv)
|
||||||
|
{
|
||||||
|
g_autoptr(GApplication) app = NULL;
|
||||||
|
gboolean terminate = FALSE;
|
||||||
|
gboolean headless = FALSE;
|
||||||
|
gboolean system = FALSE;
|
||||||
|
gboolean handover = FALSE;
|
||||||
|
GOptionEntry entries[] = {
|
||||||
|
{ "terminate", 0, 0, G_OPTION_ARG_NONE, &terminate,
|
||||||
|
"Terminate the daemon", NULL },
|
||||||
|
{ "headless", 0, 0, G_OPTION_ARG_NONE, &headless,
|
||||||
|
"Control headless daemon", NULL },
|
||||||
|
#if defined(HAVE_RDP) && defined(HAVE_LIBSYSTEMD)
|
||||||
|
{ "system", 0, 0, G_OPTION_ARG_NONE, &system,
|
||||||
|
"Control system daemon", NULL },
|
||||||
|
{ "handover", 0, 0, G_OPTION_ARG_NONE, &handover,
|
||||||
|
"Control handover daemon", NULL },
|
||||||
|
#endif /* HAVE_RDP && HAVE_LIBSYSTEMD */
|
||||||
|
{ NULL }
|
||||||
|
};
|
||||||
|
GError *error = NULL;
|
||||||
|
GOptionContext *context;
|
||||||
|
const char *app_id;
|
||||||
|
|
||||||
|
context = g_option_context_new ("- control gnome-remote-desktop");
|
||||||
|
g_option_context_add_main_entries (context, entries, NULL);
|
||||||
|
if (!g_option_context_parse (context, &argc, &argv, &error))
|
||||||
|
{
|
||||||
|
g_printerr ("Invalid option: %s\n", error->message);
|
||||||
|
g_error_free (error);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!terminate)
|
||||||
|
{
|
||||||
|
g_printerr ("%s", g_option_context_get_help (context, TRUE, NULL));
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (headless)
|
||||||
|
app_id = GRD_DAEMON_HEADLESS_APPLICATION_ID;
|
||||||
|
else if (system)
|
||||||
|
app_id = GRD_DAEMON_SYSTEM_APPLICATION_ID;
|
||||||
|
else if (handover)
|
||||||
|
app_id = GRD_DAEMON_HANDOVER_APPLICATION_ID;
|
||||||
|
else
|
||||||
|
app_id = GRD_DAEMON_USER_APPLICATION_ID;
|
||||||
|
|
||||||
|
app = g_application_new (app_id, G_APPLICATION_DEFAULT_FLAGS);
|
||||||
|
if (!g_application_register (app, NULL, NULL))
|
||||||
|
{
|
||||||
|
g_warning ("Failed to register with application\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if (!g_application_get_is_registered (app))
|
||||||
|
{
|
||||||
|
g_warning ("Not registered\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if (!g_application_get_is_remote (app))
|
||||||
|
{
|
||||||
|
g_warning ("Failed to connect to application\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (terminate)
|
||||||
|
g_action_group_activate_action (G_ACTION_GROUP (app),
|
||||||
|
"terminate", NULL);
|
||||||
|
else
|
||||||
|
g_assert_not_reached ();
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
208
grd-credentials-file.c
Normal file
208
grd-credentials-file.c
Normal file
@@ -0,0 +1,208 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2022 SUSE LLC
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License as
|
||||||
|
* published by the Free Software Foundation; either version 2 of the
|
||||||
|
* License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
|
||||||
|
* 02111-1307, USA.
|
||||||
|
*
|
||||||
|
* Written by:
|
||||||
|
* Alynx Zhou <alynx.zhou@gmail.com>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
|
#include "grd-credentials-file.h"
|
||||||
|
|
||||||
|
#include <gio/gio.h>
|
||||||
|
|
||||||
|
#define GRD_CREDENTIALS_FILE_KEY "credentials"
|
||||||
|
|
||||||
|
struct _GrdCredentialsFile
|
||||||
|
{
|
||||||
|
GrdCredentials parent;
|
||||||
|
|
||||||
|
char *filename;
|
||||||
|
GKeyFile *key_file;
|
||||||
|
};
|
||||||
|
|
||||||
|
G_DEFINE_TYPE (GrdCredentialsFile, grd_credentials_file, GRD_TYPE_CREDENTIALS)
|
||||||
|
|
||||||
|
static const char *
|
||||||
|
group_name_from_type (GrdCredentialsType type)
|
||||||
|
{
|
||||||
|
switch (type)
|
||||||
|
{
|
||||||
|
case GRD_CREDENTIALS_TYPE_RDP:
|
||||||
|
return "RDP";
|
||||||
|
case GRD_CREDENTIALS_TYPE_VNC:
|
||||||
|
return "VNC";
|
||||||
|
}
|
||||||
|
|
||||||
|
g_assert_not_reached ();
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
grd_credentials_file_store (GrdCredentials *credentials,
|
||||||
|
GrdCredentialsType type,
|
||||||
|
GVariant *variant,
|
||||||
|
GError **error)
|
||||||
|
{
|
||||||
|
GrdCredentialsFile *credentials_file = GRD_CREDENTIALS_FILE (credentials);
|
||||||
|
g_autofree const char *serialized = NULL;
|
||||||
|
g_autofree char *contents = NULL;
|
||||||
|
g_autoptr (GFile) file = NULL;
|
||||||
|
size_t length;
|
||||||
|
|
||||||
|
g_variant_ref_sink (variant);
|
||||||
|
serialized = g_variant_print (variant, TRUE);
|
||||||
|
g_variant_unref (variant);
|
||||||
|
|
||||||
|
g_key_file_set_string (credentials_file->key_file,
|
||||||
|
group_name_from_type (type),
|
||||||
|
GRD_CREDENTIALS_FILE_KEY, serialized);
|
||||||
|
|
||||||
|
contents = g_key_file_to_data (credentials_file->key_file, &length, error);
|
||||||
|
if (!contents)
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
file = g_file_new_for_path (credentials_file->filename);
|
||||||
|
return g_file_replace_contents (file, contents, length, NULL, FALSE,
|
||||||
|
G_FILE_CREATE_REPLACE_DESTINATION |
|
||||||
|
G_FILE_CREATE_PRIVATE, NULL, NULL, error);
|
||||||
|
}
|
||||||
|
|
||||||
|
static GVariant *
|
||||||
|
grd_credentials_file_lookup (GrdCredentials *credentials,
|
||||||
|
GrdCredentialsType type,
|
||||||
|
GError **error)
|
||||||
|
{
|
||||||
|
GrdCredentialsFile *credentials_file = GRD_CREDENTIALS_FILE (credentials);
|
||||||
|
g_autofree const char *serialized = NULL;
|
||||||
|
g_autoptr (GError) local_error = NULL;
|
||||||
|
|
||||||
|
serialized = g_key_file_get_string (credentials_file->key_file,
|
||||||
|
group_name_from_type (type),
|
||||||
|
GRD_CREDENTIALS_FILE_KEY, &local_error);
|
||||||
|
if (!serialized)
|
||||||
|
{
|
||||||
|
if (!g_error_matches (local_error,
|
||||||
|
G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_GROUP_NOT_FOUND) &&
|
||||||
|
!g_error_matches (local_error,
|
||||||
|
G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_KEY_NOT_FOUND))
|
||||||
|
g_propagate_error (error, g_steal_pointer (&local_error));
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return g_variant_parse (NULL, serialized, NULL, NULL, error);
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
grd_credentials_file_clear (GrdCredentials *credentials,
|
||||||
|
GrdCredentialsType type,
|
||||||
|
GError **error)
|
||||||
|
{
|
||||||
|
GrdCredentialsFile *credentials_file = GRD_CREDENTIALS_FILE (credentials);
|
||||||
|
g_autoptr (GError) local_error = NULL;
|
||||||
|
gboolean removed;
|
||||||
|
|
||||||
|
removed = g_key_file_remove_key (credentials_file->key_file,
|
||||||
|
group_name_from_type (type),
|
||||||
|
GRD_CREDENTIALS_FILE_KEY, &local_error);
|
||||||
|
if (!removed)
|
||||||
|
{
|
||||||
|
if (!g_error_matches (local_error,
|
||||||
|
G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_GROUP_NOT_FOUND) &&
|
||||||
|
!g_error_matches (local_error,
|
||||||
|
G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_KEY_NOT_FOUND))
|
||||||
|
{
|
||||||
|
g_propagate_error (error, g_steal_pointer (&local_error));
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return g_key_file_save_to_file (credentials_file->key_file,
|
||||||
|
credentials_file->filename, error);
|
||||||
|
}
|
||||||
|
|
||||||
|
GrdCredentialsFile *
|
||||||
|
grd_credentials_file_new (GError **error)
|
||||||
|
{
|
||||||
|
g_autoptr (GrdCredentialsFile) credentials_file = NULL;
|
||||||
|
g_autofree char *dir_path = NULL;
|
||||||
|
g_autoptr (GFile) dir = NULL;
|
||||||
|
|
||||||
|
credentials_file = g_object_new (GRD_TYPE_CREDENTIALS_FILE, NULL);
|
||||||
|
|
||||||
|
dir_path = g_build_path ("/",
|
||||||
|
g_get_user_data_dir (),
|
||||||
|
"gnome-remote-desktop",
|
||||||
|
NULL);
|
||||||
|
dir = g_file_new_for_path (dir_path);
|
||||||
|
if (!g_file_query_exists (dir, NULL))
|
||||||
|
{
|
||||||
|
if (!g_file_make_directory_with_parents (dir, NULL, error))
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
credentials_file->filename = g_build_path ("/",
|
||||||
|
dir_path,
|
||||||
|
"credentials.ini",
|
||||||
|
NULL);
|
||||||
|
|
||||||
|
if (!g_file_test (credentials_file->filename, G_FILE_TEST_IS_REGULAR))
|
||||||
|
if (!g_file_set_contents (credentials_file->filename, "", -1, error))
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
if (!g_key_file_load_from_file (credentials_file->key_file,
|
||||||
|
credentials_file->filename,
|
||||||
|
G_KEY_FILE_KEEP_COMMENTS | G_KEY_FILE_KEEP_TRANSLATIONS,
|
||||||
|
error))
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
return g_steal_pointer (&credentials_file);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
grd_credentials_file_finalize (GObject *object)
|
||||||
|
{
|
||||||
|
GrdCredentialsFile *credentials_file = GRD_CREDENTIALS_FILE (object);
|
||||||
|
|
||||||
|
g_clear_pointer (&credentials_file->filename, g_free);
|
||||||
|
g_clear_pointer (&credentials_file->key_file, g_key_file_unref);
|
||||||
|
|
||||||
|
G_OBJECT_CLASS (grd_credentials_file_parent_class)->finalize (object);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
grd_credentials_file_init (GrdCredentialsFile *credentials_file)
|
||||||
|
{
|
||||||
|
credentials_file->key_file = g_key_file_new ();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
grd_credentials_file_class_init (GrdCredentialsFileClass *klass)
|
||||||
|
{
|
||||||
|
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
||||||
|
GrdCredentialsClass *credentials_class = GRD_CREDENTIALS_CLASS (klass);
|
||||||
|
|
||||||
|
object_class->finalize = grd_credentials_file_finalize;
|
||||||
|
|
||||||
|
credentials_class->store = grd_credentials_file_store;
|
||||||
|
credentials_class->lookup = grd_credentials_file_lookup;
|
||||||
|
credentials_class->clear = grd_credentials_file_clear;
|
||||||
|
}
|
||||||
31
grd-credentials-file.h
Normal file
31
grd-credentials-file.h
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2022 SUSE LLC
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License as
|
||||||
|
* published by the Free Software Foundation; either version 2 of the
|
||||||
|
* License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
|
||||||
|
* 02111-1307, USA.
|
||||||
|
*
|
||||||
|
* Written by:
|
||||||
|
* Alynx Zhou <alynx.zhou@gmail.com>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "grd-credentials.h"
|
||||||
|
|
||||||
|
#define GRD_TYPE_CREDENTIALS_FILE (grd_credentials_file_get_type ())
|
||||||
|
G_DECLARE_FINAL_TYPE (GrdCredentialsFile, grd_credentials_file, GRD,
|
||||||
|
CREDENTIALS_FILE, GrdCredentials)
|
||||||
|
|
||||||
|
GrdCredentialsFile * grd_credentials_file_new (GError **error);
|
||||||
252
grd-credentials-libsecret.c
Normal file
252
grd-credentials-libsecret.c
Normal file
@@ -0,0 +1,252 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2018-2022 Red Hat Inc.
|
||||||
|
* Copyright (C) 2020 Pascal Nowack
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License as
|
||||||
|
* published by the Free Software Foundation; either version 2 of the
|
||||||
|
* License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
|
||||||
|
* 02111-1307, USA.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
|
#include "grd-credentials-libsecret.h"
|
||||||
|
|
||||||
|
#include <gio/gio.h>
|
||||||
|
#include <libsecret/secret.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
#define GRD_RDP_CREDENTIALS_SCHEMA (get_rdp_schema ())
|
||||||
|
|
||||||
|
#define GRD_VNC_PASSWORD_SCHEMA (get_vnc_schema ())
|
||||||
|
#define GRD_VNC_LEGACY_PASSWORD_SCHEMA (get_legacy_vnc_schema ())
|
||||||
|
|
||||||
|
struct _GrdCredentialsLibsecret
|
||||||
|
{
|
||||||
|
GrdCredentials parent;
|
||||||
|
};
|
||||||
|
|
||||||
|
G_DEFINE_TYPE (GrdCredentialsLibsecret,
|
||||||
|
grd_credentials_libsecret,
|
||||||
|
GRD_TYPE_CREDENTIALS)
|
||||||
|
|
||||||
|
static const SecretSchema *
|
||||||
|
get_rdp_schema (void)
|
||||||
|
{
|
||||||
|
static const SecretSchema grd_rdp_credentials_schema = {
|
||||||
|
.name = "org.gnome.RemoteDesktop.RdpCredentials",
|
||||||
|
.flags = SECRET_SCHEMA_NONE,
|
||||||
|
.attributes = {
|
||||||
|
{ "credentials", SECRET_SCHEMA_ATTRIBUTE_STRING },
|
||||||
|
{ "NULL", 0 },
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
return &grd_rdp_credentials_schema;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const SecretSchema *
|
||||||
|
get_legacy_vnc_schema (void)
|
||||||
|
{
|
||||||
|
static const SecretSchema grd_vnc_password_schema = {
|
||||||
|
.name = "org.gnome.RemoteDesktop.VncPassword",
|
||||||
|
.flags = SECRET_SCHEMA_NONE,
|
||||||
|
.attributes = {
|
||||||
|
{ "password", SECRET_SCHEMA_ATTRIBUTE_STRING },
|
||||||
|
{ "NULL", 0 },
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
return &grd_vnc_password_schema;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const SecretSchema *
|
||||||
|
get_vnc_schema (void)
|
||||||
|
{
|
||||||
|
static const SecretSchema grd_vnc_password_schema = {
|
||||||
|
.name = "org.gnome.RemoteDesktop.VncCredentials",
|
||||||
|
.flags = SECRET_SCHEMA_NONE,
|
||||||
|
.attributes = {
|
||||||
|
{ "password", SECRET_SCHEMA_ATTRIBUTE_STRING },
|
||||||
|
{ "NULL", 0 },
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
return &grd_vnc_password_schema;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const SecretSchema *
|
||||||
|
schema_from_type (GrdCredentialsType type)
|
||||||
|
{
|
||||||
|
switch (type)
|
||||||
|
{
|
||||||
|
case GRD_CREDENTIALS_TYPE_RDP:
|
||||||
|
return GRD_RDP_CREDENTIALS_SCHEMA;
|
||||||
|
case GRD_CREDENTIALS_TYPE_VNC:
|
||||||
|
return GRD_VNC_PASSWORD_SCHEMA;
|
||||||
|
}
|
||||||
|
|
||||||
|
g_assert_not_reached ();
|
||||||
|
}
|
||||||
|
|
||||||
|
static const char *
|
||||||
|
description_from_type (GrdCredentialsType type)
|
||||||
|
{
|
||||||
|
switch (type)
|
||||||
|
{
|
||||||
|
case GRD_CREDENTIALS_TYPE_RDP:
|
||||||
|
return "GNOME Remote Desktop RDP credentials";
|
||||||
|
case GRD_CREDENTIALS_TYPE_VNC:
|
||||||
|
return "GNOME Remote Desktop VNC password";
|
||||||
|
}
|
||||||
|
|
||||||
|
g_assert_not_reached ();
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
grd_credentials_libsecret_store (GrdCredentials *credentials,
|
||||||
|
GrdCredentialsType type,
|
||||||
|
GVariant *variant,
|
||||||
|
GError **error)
|
||||||
|
{
|
||||||
|
g_autofree char *serialized = NULL;
|
||||||
|
|
||||||
|
g_variant_ref_sink (variant);
|
||||||
|
serialized = g_variant_print (variant, TRUE);
|
||||||
|
g_variant_unref (variant);
|
||||||
|
|
||||||
|
return secret_password_store_sync (schema_from_type (type),
|
||||||
|
SECRET_COLLECTION_DEFAULT,
|
||||||
|
description_from_type (type),
|
||||||
|
serialized,
|
||||||
|
NULL, error,
|
||||||
|
NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
static GVariant *
|
||||||
|
grd_credentials_libsecret_lookup (GrdCredentials *credentials,
|
||||||
|
GrdCredentialsType type,
|
||||||
|
GError **error)
|
||||||
|
{
|
||||||
|
g_autofree char *serialized = NULL;
|
||||||
|
|
||||||
|
serialized = secret_password_lookup_sync (schema_from_type (type),
|
||||||
|
NULL, error,
|
||||||
|
NULL);
|
||||||
|
if (!serialized)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
return g_variant_parse (NULL, serialized, NULL, NULL, error);
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
grd_credentials_libsecret_clear (GrdCredentials *credentials,
|
||||||
|
GrdCredentialsType type,
|
||||||
|
GError **error)
|
||||||
|
{
|
||||||
|
g_autoptr (GError) local_error = NULL;
|
||||||
|
|
||||||
|
secret_password_clear_sync (schema_from_type (type),
|
||||||
|
NULL, &local_error,
|
||||||
|
NULL);
|
||||||
|
if (local_error)
|
||||||
|
{
|
||||||
|
g_propagate_error (error, g_steal_pointer (&local_error));
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
GrdCredentialsLibsecret *
|
||||||
|
grd_credentials_libsecret_new (void)
|
||||||
|
{
|
||||||
|
return g_object_new (GRD_TYPE_CREDENTIALS_LIBSECRET, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
grd_credentials_libsecret_init (GrdCredentialsLibsecret *credentials_libsecret)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
maybe_migrate_legacy_vnc_password (GrdCredentials *credentials)
|
||||||
|
{
|
||||||
|
g_autoptr (GError) error = NULL;
|
||||||
|
g_autofree char *password = NULL;
|
||||||
|
|
||||||
|
password = secret_password_lookup_sync (GRD_VNC_LEGACY_PASSWORD_SCHEMA,
|
||||||
|
NULL, &error,
|
||||||
|
NULL);
|
||||||
|
if (!password)
|
||||||
|
{
|
||||||
|
if (error)
|
||||||
|
{
|
||||||
|
g_printerr ("Failed to lookup legacy VNC password schema: %s\n",
|
||||||
|
error->message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
g_printerr ("Migrating VNC password to new schema... ");
|
||||||
|
|
||||||
|
if (!grd_credentials_store (credentials,
|
||||||
|
GRD_CREDENTIALS_TYPE_VNC,
|
||||||
|
g_variant_new_string (password),
|
||||||
|
&error))
|
||||||
|
{
|
||||||
|
g_printerr ("Failed to migrate VNC password to new schema: %s\n",
|
||||||
|
error->message);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (!secret_password_clear_sync (GRD_VNC_LEGACY_PASSWORD_SCHEMA,
|
||||||
|
NULL, &error, NULL) &&
|
||||||
|
error)
|
||||||
|
{
|
||||||
|
g_printerr ("Failed to clear VNC password from old schema: %s\n",
|
||||||
|
error->message);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
g_printerr ("OK\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
grd_credentials_libsecret_constructed (GObject *object)
|
||||||
|
{
|
||||||
|
GrdCredentials *credentials = GRD_CREDENTIALS (object);
|
||||||
|
|
||||||
|
maybe_migrate_legacy_vnc_password (credentials);
|
||||||
|
|
||||||
|
G_OBJECT_CLASS (grd_credentials_libsecret_parent_class)->constructed (object);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
grd_credentials_libsecret_class_init (GrdCredentialsLibsecretClass *klass)
|
||||||
|
{
|
||||||
|
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
||||||
|
GrdCredentialsClass *credentials_class = GRD_CREDENTIALS_CLASS (klass);
|
||||||
|
|
||||||
|
object_class->constructed = grd_credentials_libsecret_constructed;
|
||||||
|
|
||||||
|
credentials_class->store = grd_credentials_libsecret_store;
|
||||||
|
credentials_class->lookup = grd_credentials_libsecret_lookup;
|
||||||
|
credentials_class->clear = grd_credentials_libsecret_clear;
|
||||||
|
}
|
||||||
30
grd-credentials-libsecret.h
Normal file
30
grd-credentials-libsecret.h
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2018-2022 Red Hat Inc.
|
||||||
|
* Copyright (C) 2020 Pascal Nowack
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License as
|
||||||
|
* published by the Free Software Foundation; either version 2 of the
|
||||||
|
* License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
|
||||||
|
* 02111-1307, USA.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "grd-credentials.h"
|
||||||
|
|
||||||
|
#define GRD_TYPE_CREDENTIALS_LIBSECRET (grd_credentials_libsecret_get_type ())
|
||||||
|
G_DECLARE_FINAL_TYPE (GrdCredentialsLibsecret, grd_credentials_libsecret,
|
||||||
|
GRD, CREDENTIALS_LIBSECRET, GrdCredentials)
|
||||||
|
|
||||||
|
GrdCredentialsLibsecret * grd_credentials_libsecret_new (void);
|
||||||
232
grd-credentials-one-time.c
Normal file
232
grd-credentials-one-time.c
Normal file
@@ -0,0 +1,232 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2022 SUSE Software Solutions Germany GmbH
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License as
|
||||||
|
* published by the Free Software Foundation; either version 2 of the
|
||||||
|
* License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
|
||||||
|
* 02111-1307, USA.
|
||||||
|
*
|
||||||
|
* Written by:
|
||||||
|
* Joan Torres <joan.torres@suse.com>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <gio/gio.h>
|
||||||
|
|
||||||
|
#include "grd-credentials-one-time.h"
|
||||||
|
|
||||||
|
struct _GrdCredentialsOneTime
|
||||||
|
{
|
||||||
|
GrdCredentials parent;
|
||||||
|
|
||||||
|
struct
|
||||||
|
{
|
||||||
|
char *username;
|
||||||
|
char *password;
|
||||||
|
} rdp;
|
||||||
|
};
|
||||||
|
|
||||||
|
G_DEFINE_TYPE (GrdCredentialsOneTime,
|
||||||
|
grd_credentials_one_time,
|
||||||
|
GRD_TYPE_CREDENTIALS)
|
||||||
|
|
||||||
|
static char *
|
||||||
|
grd_generate_random_bytes (size_t size,
|
||||||
|
GError **error)
|
||||||
|
{
|
||||||
|
int fd;
|
||||||
|
int ret;
|
||||||
|
char *bytes;
|
||||||
|
|
||||||
|
errno = 0;
|
||||||
|
fd = open ("/dev/urandom", O_RDONLY);
|
||||||
|
if (fd < 0)
|
||||||
|
{
|
||||||
|
g_set_error_literal (error,
|
||||||
|
G_FILE_ERROR,
|
||||||
|
g_file_error_from_errno (errno),
|
||||||
|
g_strerror (errno));
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
bytes = g_malloc (size);
|
||||||
|
do
|
||||||
|
ret = read (fd, bytes, size);
|
||||||
|
while ((ret == -1 &&
|
||||||
|
(errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK)) ||
|
||||||
|
ret < size);
|
||||||
|
|
||||||
|
close (fd);
|
||||||
|
|
||||||
|
return bytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
static char *
|
||||||
|
generate_random_utf8_bytes (size_t len,
|
||||||
|
GError **error)
|
||||||
|
{
|
||||||
|
char *random_bytes;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
random_bytes = grd_generate_random_bytes (len + 1, error);
|
||||||
|
if (!random_bytes)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
/* UTF-8 chars defined with 1 byte always have the MSB to 0.
|
||||||
|
* Do not use ASCII control characters (0 - 32, 127). */
|
||||||
|
for (i = 0; i < len; ++i)
|
||||||
|
{
|
||||||
|
random_bytes[i] &= 127;
|
||||||
|
random_bytes[i] = (random_bytes[i] % 94) + 33;
|
||||||
|
}
|
||||||
|
random_bytes[len] = '\0';
|
||||||
|
|
||||||
|
return random_bytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
static char *
|
||||||
|
generate_random_username (size_t len,
|
||||||
|
GError **error)
|
||||||
|
{
|
||||||
|
char *username;
|
||||||
|
|
||||||
|
username = generate_random_utf8_bytes (len, error);
|
||||||
|
if (!username)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
/* The use of # at the beggining or : at any position,
|
||||||
|
* makes an error when looking up the user on the SAM file. */
|
||||||
|
return g_strdelimit (username, "#:", '_');
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
grd_credentials_one_time_store (GrdCredentials *credentials,
|
||||||
|
GrdCredentialsType type,
|
||||||
|
GVariant *variant,
|
||||||
|
GError **error)
|
||||||
|
{
|
||||||
|
GrdCredentialsOneTime *credentials_one_time =
|
||||||
|
GRD_CREDENTIALS_ONE_TIME (credentials);
|
||||||
|
g_autofree char *username = NULL;
|
||||||
|
g_autofree char *password = NULL;
|
||||||
|
|
||||||
|
g_assert (type == GRD_CREDENTIALS_TYPE_RDP);
|
||||||
|
|
||||||
|
g_variant_lookup (variant, "username", "s", &username);
|
||||||
|
if (!username)
|
||||||
|
{
|
||||||
|
g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND,
|
||||||
|
"Username not set");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
g_variant_lookup (variant, "password", "s", &password);
|
||||||
|
if (!password)
|
||||||
|
{
|
||||||
|
g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND,
|
||||||
|
"Password not set");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
g_clear_pointer (&credentials_one_time->rdp.username, g_free);
|
||||||
|
g_clear_pointer (&credentials_one_time->rdp.password, g_free);
|
||||||
|
|
||||||
|
credentials_one_time->rdp.username = g_steal_pointer (&username);
|
||||||
|
credentials_one_time->rdp.password = g_steal_pointer (&password);
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static GVariant *
|
||||||
|
grd_credentials_one_time_lookup (GrdCredentials *credentials,
|
||||||
|
GrdCredentialsType type,
|
||||||
|
GError **error)
|
||||||
|
{
|
||||||
|
GrdCredentialsOneTime *credentials_one_time =
|
||||||
|
GRD_CREDENTIALS_ONE_TIME (credentials);
|
||||||
|
const char *rdp_username = credentials_one_time->rdp.username;
|
||||||
|
const char *rdp_password = credentials_one_time->rdp.password;
|
||||||
|
GVariantBuilder builder;
|
||||||
|
|
||||||
|
switch (type)
|
||||||
|
{
|
||||||
|
case GRD_CREDENTIALS_TYPE_RDP:
|
||||||
|
g_variant_builder_init (&builder, G_VARIANT_TYPE ("a{sv}"));
|
||||||
|
|
||||||
|
g_variant_builder_add (&builder, "{sv}",
|
||||||
|
"username", g_variant_new_string (rdp_username));
|
||||||
|
g_variant_builder_add (&builder, "{sv}",
|
||||||
|
"password", g_variant_new_string (rdp_password));
|
||||||
|
|
||||||
|
return g_variant_builder_end (&builder);
|
||||||
|
default:
|
||||||
|
g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND,
|
||||||
|
"Credentials type not found");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
GrdCredentialsOneTime *
|
||||||
|
grd_credentials_one_time_new (void)
|
||||||
|
{
|
||||||
|
g_autoptr (GrdCredentialsOneTime) credentials_one_time = NULL;
|
||||||
|
g_autoptr (GError) error = NULL;
|
||||||
|
|
||||||
|
credentials_one_time = g_object_new (GRD_TYPE_CREDENTIALS_ONE_TIME, NULL);
|
||||||
|
|
||||||
|
credentials_one_time->rdp.username = generate_random_username (16, &error);
|
||||||
|
if (!credentials_one_time->rdp.username)
|
||||||
|
{
|
||||||
|
g_warning ("Failed to generate one time RDP username: %s", error->message);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
credentials_one_time->rdp.password = generate_random_utf8_bytes (16, &error);
|
||||||
|
if (!credentials_one_time->rdp.password)
|
||||||
|
{
|
||||||
|
g_warning ("Failed to generate one time RDP password: %s", error->message);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return g_steal_pointer (&credentials_one_time);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
grd_credentials_one_time_finalize (GObject *object)
|
||||||
|
{
|
||||||
|
GrdCredentialsOneTime *credentials_one_time = GRD_CREDENTIALS_ONE_TIME (object);
|
||||||
|
|
||||||
|
g_clear_pointer (&credentials_one_time->rdp.username, g_free);
|
||||||
|
g_clear_pointer (&credentials_one_time->rdp.password, g_free);
|
||||||
|
|
||||||
|
G_OBJECT_CLASS (grd_credentials_one_time_parent_class)->finalize (object);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
grd_credentials_one_time_init (GrdCredentialsOneTime *credentials_one_time)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
grd_credentials_one_time_class_init (GrdCredentialsOneTimeClass *klass)
|
||||||
|
{
|
||||||
|
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
||||||
|
GrdCredentialsClass *credentials_class = GRD_CREDENTIALS_CLASS (klass);
|
||||||
|
|
||||||
|
object_class->finalize = grd_credentials_one_time_finalize;
|
||||||
|
|
||||||
|
credentials_class->store = grd_credentials_one_time_store;
|
||||||
|
credentials_class->lookup = grd_credentials_one_time_lookup;
|
||||||
|
}
|
||||||
31
grd-credentials-one-time.h
Normal file
31
grd-credentials-one-time.h
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2022 SUSE Software Solutions Germany GmbH
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License as
|
||||||
|
* published by the Free Software Foundation; either version 2 of the
|
||||||
|
* License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
|
||||||
|
* 02111-1307, USA.
|
||||||
|
*
|
||||||
|
* Written by:
|
||||||
|
* Joan Torres <joan.torres@suse.com>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "grd-credentials.h"
|
||||||
|
|
||||||
|
#define GRD_TYPE_CREDENTIALS_ONE_TIME (grd_credentials_one_time_get_type ())
|
||||||
|
G_DECLARE_FINAL_TYPE (GrdCredentialsOneTime, grd_credentials_one_time,
|
||||||
|
GRD, CREDENTIALS_ONE_TIME, GrdCredentials)
|
||||||
|
|
||||||
|
GrdCredentialsOneTime *grd_credentials_one_time_new (void);
|
||||||
243
grd-credentials-tpm.c
Normal file
243
grd-credentials-tpm.c
Normal file
@@ -0,0 +1,243 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2022 Red Hat Inc.
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License as
|
||||||
|
* published by the Free Software Foundation; either version 2 of the
|
||||||
|
* License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
|
||||||
|
* 02111-1307, USA.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
|
#include "grd-credentials-tpm.h"
|
||||||
|
|
||||||
|
#include <gio/gio.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "grd-tpm.h"
|
||||||
|
|
||||||
|
struct _GrdCredentialsTpm
|
||||||
|
{
|
||||||
|
GrdCredentials parent;
|
||||||
|
};
|
||||||
|
|
||||||
|
G_DEFINE_TYPE (GrdCredentialsTpm, grd_credentials_tpm, GRD_TYPE_CREDENTIALS)
|
||||||
|
|
||||||
|
static const char *
|
||||||
|
secret_file_name_from_type (GrdCredentialsType type)
|
||||||
|
{
|
||||||
|
switch (type)
|
||||||
|
{
|
||||||
|
case GRD_CREDENTIALS_TYPE_RDP:
|
||||||
|
return "rdp-credentials.priv";
|
||||||
|
case GRD_CREDENTIALS_TYPE_VNC:
|
||||||
|
return "vnc-credentials.priv";
|
||||||
|
}
|
||||||
|
|
||||||
|
g_assert_not_reached ();
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
grd_credentials_tpm_store (GrdCredentials *credentials,
|
||||||
|
GrdCredentialsType type,
|
||||||
|
GVariant *variant,
|
||||||
|
GError **error)
|
||||||
|
{
|
||||||
|
g_autoptr (GrdTpm) tpm = NULL;
|
||||||
|
g_autofree const char *serialized = NULL;
|
||||||
|
ESYS_TR primary_handle = 0;
|
||||||
|
g_autofree TPMS_CONTEXT *primary = NULL;
|
||||||
|
g_autofree TPML_PCR_SELECTION *pcr_selection = NULL;
|
||||||
|
g_autofree TPML_DIGEST *pcr_digest = NULL;
|
||||||
|
g_autofree TPMS_CONTEXT *secret_tpms_context = NULL;
|
||||||
|
g_autoptr (GVariant) secret_variant = NULL;
|
||||||
|
g_autofree char *secret_serialized = NULL;
|
||||||
|
g_autofree char *dir_path = NULL;
|
||||||
|
g_autoptr (GFile) dir = NULL;
|
||||||
|
const char *secret_file_name;
|
||||||
|
g_autofree char *secret_path = NULL;
|
||||||
|
g_autoptr (GFile) secret_file = NULL;
|
||||||
|
|
||||||
|
g_variant_ref_sink (variant);
|
||||||
|
serialized = g_variant_print (variant, TRUE);
|
||||||
|
g_variant_unref (variant);
|
||||||
|
|
||||||
|
tpm = grd_tpm_new (GRD_TPM_MODE_WRITE, error);
|
||||||
|
if (!tpm)
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
if (!grd_tpm_create_primary (tpm, &primary_handle, error))
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
if (!grd_tpm_read_pcr (tpm, &pcr_selection, &pcr_digest, error))
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
if (!grd_tpm_store_secret (tpm,
|
||||||
|
serialized,
|
||||||
|
primary_handle,
|
||||||
|
pcr_selection,
|
||||||
|
pcr_digest,
|
||||||
|
&secret_tpms_context,
|
||||||
|
error))
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
secret_variant = grd_tpms_context_to_variant (secret_tpms_context);
|
||||||
|
|
||||||
|
dir_path = g_build_path ("/",
|
||||||
|
g_get_user_data_dir (),
|
||||||
|
"gnome-remote-desktop",
|
||||||
|
NULL);
|
||||||
|
dir = g_file_new_for_path (dir_path);
|
||||||
|
if (!g_file_query_exists (dir, NULL))
|
||||||
|
{
|
||||||
|
if (!g_file_make_directory_with_parents (dir, NULL, error))
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
secret_file_name = secret_file_name_from_type (type);
|
||||||
|
secret_path = g_build_path ("/", dir_path, secret_file_name, NULL);
|
||||||
|
secret_file = g_file_new_for_path (secret_path);
|
||||||
|
|
||||||
|
secret_serialized = g_variant_print (secret_variant, TRUE);
|
||||||
|
|
||||||
|
return g_file_replace_contents (secret_file,
|
||||||
|
secret_serialized,
|
||||||
|
strlen (secret_serialized) + 1,
|
||||||
|
NULL, FALSE,
|
||||||
|
G_FILE_CREATE_PRIVATE |
|
||||||
|
G_FILE_CREATE_REPLACE_DESTINATION,
|
||||||
|
NULL, NULL, error);
|
||||||
|
}
|
||||||
|
|
||||||
|
static GVariant *
|
||||||
|
grd_credentials_tpm_lookup (GrdCredentials *credentials,
|
||||||
|
GrdCredentialsType type,
|
||||||
|
GError **error)
|
||||||
|
{
|
||||||
|
g_autoptr (GrdTpm) tpm = NULL;
|
||||||
|
const char *secret_file_name;
|
||||||
|
g_autofree char *secret_path = NULL;
|
||||||
|
g_autofree char *serialized = NULL;
|
||||||
|
g_autofree TPML_PCR_SELECTION *pcr_selection = NULL;
|
||||||
|
g_autofree TPML_DIGEST *pcr_digest = NULL;
|
||||||
|
g_autoptr (GVariant) secret_variant = NULL;
|
||||||
|
g_autoptr (GError) local_error = NULL;
|
||||||
|
g_autofree char *credentials_string = NULL;
|
||||||
|
|
||||||
|
secret_file_name = secret_file_name_from_type (type);
|
||||||
|
secret_path = g_build_path ("/",
|
||||||
|
g_get_user_data_dir (),
|
||||||
|
"gnome-remote-desktop",
|
||||||
|
secret_file_name,
|
||||||
|
NULL);
|
||||||
|
|
||||||
|
if (!g_file_get_contents (secret_path,
|
||||||
|
&serialized,
|
||||||
|
NULL,
|
||||||
|
&local_error))
|
||||||
|
{
|
||||||
|
if (g_error_matches (local_error, G_FILE_ERROR, G_FILE_ERROR_NOENT))
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
g_propagate_error (error, g_steal_pointer (&local_error));
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
secret_variant = g_variant_parse (G_VARIANT_TYPE ("(uutqs)"), serialized,
|
||||||
|
NULL, NULL, error);
|
||||||
|
if (!secret_variant)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
tpm = grd_tpm_new (GRD_TPM_MODE_READ, error);
|
||||||
|
if (!tpm)
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
if (!grd_tpm_read_pcr (tpm, &pcr_selection, &pcr_digest, error))
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
credentials_string = grd_tpm_restore_secret (tpm,
|
||||||
|
secret_variant,
|
||||||
|
pcr_selection,
|
||||||
|
pcr_digest,
|
||||||
|
error);
|
||||||
|
if (!credentials_string)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
return g_variant_parse (NULL, credentials_string, NULL, NULL, error);
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
grd_credentials_tpm_clear (GrdCredentials *credentials,
|
||||||
|
GrdCredentialsType type,
|
||||||
|
GError **error)
|
||||||
|
{
|
||||||
|
const char *secret_file_name;
|
||||||
|
g_autofree char *secret_path = NULL;
|
||||||
|
g_autoptr (GFile) secret_file = NULL;
|
||||||
|
g_autoptr (GError) local_error = NULL;
|
||||||
|
|
||||||
|
secret_file_name = secret_file_name_from_type (type);
|
||||||
|
secret_path = g_build_path ("/",
|
||||||
|
g_get_user_data_dir (),
|
||||||
|
"gnome-remote-desktop",
|
||||||
|
secret_file_name,
|
||||||
|
NULL);
|
||||||
|
secret_file = g_file_new_for_path (secret_path);
|
||||||
|
|
||||||
|
if (!g_file_delete (secret_file, NULL, error))
|
||||||
|
{
|
||||||
|
if (g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND))
|
||||||
|
{
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
g_propagate_error (error, g_steal_pointer (&local_error));
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
GrdCredentialsTpm *
|
||||||
|
grd_credentials_tpm_new (GError **error)
|
||||||
|
{
|
||||||
|
g_autoptr (GrdTpm) tpm = NULL;
|
||||||
|
|
||||||
|
tpm = grd_tpm_new (GRD_TPM_MODE_NONE, error);
|
||||||
|
if (!tpm)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
if (!grd_tpm_check_capabilities (tpm, error))
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
return g_object_new (GRD_TYPE_CREDENTIALS_TPM, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
grd_credentials_tpm_init (GrdCredentialsTpm *credentials_tpm)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
grd_credentials_tpm_class_init (GrdCredentialsTpmClass *klass)
|
||||||
|
{
|
||||||
|
GrdCredentialsClass *credentials_class = GRD_CREDENTIALS_CLASS (klass);
|
||||||
|
|
||||||
|
credentials_class->store = grd_credentials_tpm_store;
|
||||||
|
credentials_class->lookup = grd_credentials_tpm_lookup;
|
||||||
|
credentials_class->clear = grd_credentials_tpm_clear;
|
||||||
|
}
|
||||||
29
grd-credentials-tpm.h
Normal file
29
grd-credentials-tpm.h
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2022 Red Hat Inc.
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License as
|
||||||
|
* published by the Free Software Foundation; either version 2 of the
|
||||||
|
* License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
|
||||||
|
* 02111-1307, USA.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "grd-credentials.h"
|
||||||
|
|
||||||
|
#define GRD_TYPE_CREDENTIALS_TPM (grd_credentials_tpm_get_type ())
|
||||||
|
G_DECLARE_FINAL_TYPE (GrdCredentialsTpm, grd_credentials_tpm,
|
||||||
|
GRD, CREDENTIALS_TPM, GrdCredentials)
|
||||||
|
|
||||||
|
GrdCredentialsTpm * grd_credentials_tpm_new (GError **error);
|
||||||
63
grd-credentials.c
Normal file
63
grd-credentials.c
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2022 Red Hat Inc.
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License as
|
||||||
|
* published by the Free Software Foundation; either version 2 of the
|
||||||
|
* License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
|
||||||
|
* 02111-1307, USA.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
|
#include "grd-credentials.h"
|
||||||
|
|
||||||
|
G_DEFINE_ABSTRACT_TYPE (GrdCredentials, grd_credentials, G_TYPE_OBJECT)
|
||||||
|
|
||||||
|
GVariant *
|
||||||
|
grd_credentials_lookup (GrdCredentials *credentials,
|
||||||
|
GrdCredentialsType type,
|
||||||
|
GError **error)
|
||||||
|
{
|
||||||
|
return GRD_CREDENTIALS_GET_CLASS (credentials)->lookup (credentials,
|
||||||
|
type, error);
|
||||||
|
}
|
||||||
|
|
||||||
|
gboolean
|
||||||
|
grd_credentials_clear (GrdCredentials *credentials,
|
||||||
|
GrdCredentialsType type,
|
||||||
|
GError **error)
|
||||||
|
{
|
||||||
|
return GRD_CREDENTIALS_GET_CLASS (credentials)->clear (credentials,
|
||||||
|
type, error);
|
||||||
|
}
|
||||||
|
|
||||||
|
gboolean
|
||||||
|
grd_credentials_store (GrdCredentials *credentials,
|
||||||
|
GrdCredentialsType type,
|
||||||
|
GVariant *variant,
|
||||||
|
GError **error)
|
||||||
|
{
|
||||||
|
return GRD_CREDENTIALS_GET_CLASS (credentials)->store (credentials,
|
||||||
|
type, variant, error);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
grd_credentials_init (GrdCredentials *credentials)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
grd_credentials_class_init (GrdCredentialsClass *klass)
|
||||||
|
{
|
||||||
|
}
|
||||||
64
grd-credentials.h
Normal file
64
grd-credentials.h
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2022 Red Hat Inc.
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License as
|
||||||
|
* published by the Free Software Foundation; either version 2 of the
|
||||||
|
* License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
|
||||||
|
* 02111-1307, USA.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <glib-object.h>
|
||||||
|
|
||||||
|
typedef enum _GrdCredentialsType
|
||||||
|
{
|
||||||
|
GRD_CREDENTIALS_TYPE_RDP,
|
||||||
|
GRD_CREDENTIALS_TYPE_VNC,
|
||||||
|
} GrdCredentialsType;
|
||||||
|
|
||||||
|
#define GRD_TYPE_CREDENTIALS (grd_credentials_get_type ())
|
||||||
|
G_DECLARE_DERIVABLE_TYPE (GrdCredentials, grd_credentials,
|
||||||
|
GRD, CREDENTIALS, GObject)
|
||||||
|
|
||||||
|
struct _GrdCredentialsClass
|
||||||
|
{
|
||||||
|
GObjectClass parent_class;
|
||||||
|
|
||||||
|
gboolean (* store) (GrdCredentials *credentials,
|
||||||
|
GrdCredentialsType type,
|
||||||
|
GVariant *variant,
|
||||||
|
GError **error);
|
||||||
|
|
||||||
|
GVariant * (* lookup) (GrdCredentials *credentials,
|
||||||
|
GrdCredentialsType type,
|
||||||
|
GError **error);
|
||||||
|
|
||||||
|
gboolean (* clear) (GrdCredentials *credentials,
|
||||||
|
GrdCredentialsType type,
|
||||||
|
GError **error);
|
||||||
|
};
|
||||||
|
|
||||||
|
gboolean grd_credentials_store (GrdCredentials *credentials,
|
||||||
|
GrdCredentialsType type,
|
||||||
|
GVariant *variant,
|
||||||
|
GError **error);
|
||||||
|
|
||||||
|
GVariant * grd_credentials_lookup (GrdCredentials *credentials,
|
||||||
|
GrdCredentialsType type,
|
||||||
|
GError **error);
|
||||||
|
|
||||||
|
gboolean grd_credentials_clear (GrdCredentials *credentials,
|
||||||
|
GrdCredentialsType type,
|
||||||
|
GError **error);
|
||||||
178
grd-cuda-avc-utils.cu
Normal file
178
grd-cuda-avc-utils.cu
Normal file
@@ -0,0 +1,178 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2021 Pascal Nowack
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License as
|
||||||
|
* published by the Free Software Foundation; either version 2 of the
|
||||||
|
* License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
|
||||||
|
* 02111-1307, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Generate the PTX instructions with:
|
||||||
|
* clang --cuda-gpu-arch=sm_30 -S src/grd-cuda-avc-utils.cu -o data/grd-cuda-avc-utils_30.ptx --no-cuda-version-check -O3 --cuda-device-only -Wall -Wextra
|
||||||
|
*
|
||||||
|
* or
|
||||||
|
*
|
||||||
|
* nvcc -arch=compute_30 -ptx grd-cuda-avc-utils.cu -o grd-cuda-avc-utils_30.ptx
|
||||||
|
*
|
||||||
|
* Note: This requires CUDA < 11, since the generation of Kepler capable
|
||||||
|
* PTX code was removed from CUDA 11.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
extern "C"
|
||||||
|
{
|
||||||
|
__device__ uint8_t
|
||||||
|
rgb_to_y (uint8_t r,
|
||||||
|
uint8_t g,
|
||||||
|
uint8_t b)
|
||||||
|
{
|
||||||
|
return (54 * r + 183 * g + 18 * b) >> 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
__device__ uint8_t
|
||||||
|
rgb_to_u (uint8_t r,
|
||||||
|
uint8_t g,
|
||||||
|
uint8_t b)
|
||||||
|
{
|
||||||
|
return ((-29 * r - 99 * g + 128 * b) >> 8) + 128;
|
||||||
|
}
|
||||||
|
|
||||||
|
__device__ uint8_t
|
||||||
|
rgb_to_v (uint8_t r,
|
||||||
|
uint8_t g,
|
||||||
|
uint8_t b)
|
||||||
|
{
|
||||||
|
return ((128 * r - 116 * g - 12 * b) >> 8) + 128;
|
||||||
|
}
|
||||||
|
|
||||||
|
__global__ void
|
||||||
|
convert_2x2_bgrx_area_to_yuv420_nv12 (uint8_t *dst_data,
|
||||||
|
uint32_t *src_data,
|
||||||
|
uint16_t src_width,
|
||||||
|
uint16_t src_height,
|
||||||
|
uint16_t aligned_width,
|
||||||
|
uint16_t aligned_height,
|
||||||
|
uint16_t aligned_stride)
|
||||||
|
{
|
||||||
|
uint8_t *dst_y0, *dst_y1, *dst_y2, *dst_y3, *dst_u, *dst_v;
|
||||||
|
uint32_t *src_u32;
|
||||||
|
uint16_t s0, s1, s2, s3;
|
||||||
|
uint32_t bgrx;
|
||||||
|
int32_t r_a, g_a, b_a;
|
||||||
|
uint8_t r, g, b;
|
||||||
|
uint16_t x_1x1, y_1x1;
|
||||||
|
uint16_t x_2x2, y_2x2;
|
||||||
|
|
||||||
|
x_2x2 = blockIdx.x * blockDim.x + threadIdx.x;
|
||||||
|
y_2x2 = blockIdx.y * blockDim.y + threadIdx.y;
|
||||||
|
|
||||||
|
if (x_2x2 >= aligned_width >> 1 || y_2x2 >= aligned_height >> 1)
|
||||||
|
return;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* -------------
|
||||||
|
* | d_0 | d_1 |
|
||||||
|
* -------------
|
||||||
|
* | d_2 | d_3 |
|
||||||
|
* -------------
|
||||||
|
*/
|
||||||
|
s0 = 0;
|
||||||
|
s1 = 1;
|
||||||
|
s2 = src_width;
|
||||||
|
s3 = src_width + 1;
|
||||||
|
|
||||||
|
x_1x1 = x_2x2 << 1;
|
||||||
|
y_1x1 = y_2x2 << 1;
|
||||||
|
src_u32 = src_data + y_1x1 * src_width + x_1x1;
|
||||||
|
|
||||||
|
dst_y0 = dst_data + y_1x1 * aligned_stride + x_1x1;
|
||||||
|
dst_y1 = dst_y0 + 1;
|
||||||
|
dst_y2 = dst_data + (y_1x1 + 1) * aligned_stride + x_1x1;
|
||||||
|
dst_y3 = dst_y2 + 1;
|
||||||
|
dst_u = dst_data + aligned_height * aligned_stride +
|
||||||
|
y_2x2 * aligned_stride + x_1x1;
|
||||||
|
dst_v = dst_u + 1;
|
||||||
|
|
||||||
|
/* d_0 */
|
||||||
|
if (x_1x1 < src_width && y_1x1 < src_height)
|
||||||
|
{
|
||||||
|
bgrx = src_u32[s0];
|
||||||
|
|
||||||
|
b_a = b = *(((uint8_t *) &bgrx) + 0);
|
||||||
|
g_a = g = *(((uint8_t *) &bgrx) + 1);
|
||||||
|
r_a = r = *(((uint8_t *) &bgrx) + 2);
|
||||||
|
*dst_y0 = rgb_to_y (r, g, b);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
b_a = b = 0;
|
||||||
|
g_a = g = 0;
|
||||||
|
r_a = r = 0;
|
||||||
|
*dst_y0 = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (x_1x1 + 1 < src_width && y_1x1 < src_height)
|
||||||
|
{
|
||||||
|
bgrx = src_u32[s1];
|
||||||
|
|
||||||
|
/* d_1 */
|
||||||
|
b_a += b = *(((uint8_t *) &bgrx) + 0);
|
||||||
|
g_a += g = *(((uint8_t *) &bgrx) + 1);
|
||||||
|
r_a += r = *(((uint8_t *) &bgrx) + 2);
|
||||||
|
*dst_y1 = rgb_to_y (r, g, b);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
*dst_y1 = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (x_1x1 < src_width && y_1x1 + 1 < src_height)
|
||||||
|
{
|
||||||
|
bgrx = src_u32[s2];
|
||||||
|
|
||||||
|
/* d_2 */
|
||||||
|
b_a += b = *(((uint8_t *) &bgrx) + 0);
|
||||||
|
g_a += g = *(((uint8_t *) &bgrx) + 1);
|
||||||
|
r_a += r = *(((uint8_t *) &bgrx) + 2);
|
||||||
|
*dst_y2 = rgb_to_y (r, g, b);
|
||||||
|
|
||||||
|
if (x_1x1 + 1 < src_width)
|
||||||
|
{
|
||||||
|
bgrx = src_u32[s3];
|
||||||
|
|
||||||
|
/* d_3 */
|
||||||
|
b_a += b = *(((uint8_t *) &bgrx) + 0);
|
||||||
|
g_a += g = *(((uint8_t *) &bgrx) + 1);
|
||||||
|
r_a += r = *(((uint8_t *) &bgrx) + 2);
|
||||||
|
*dst_y3 = rgb_to_y (r, g, b);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
*dst_y3 = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
*dst_y2 = 0;
|
||||||
|
*dst_y3 = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
b_a >>= 2;
|
||||||
|
g_a >>= 2;
|
||||||
|
r_a >>= 2;
|
||||||
|
*dst_u = rgb_to_u (r_a, g_a, b_a);
|
||||||
|
*dst_v = rgb_to_v (r_a, g_a, b_a);
|
||||||
|
}
|
||||||
|
}
|
||||||
138
grd-cuda-damage-utils.cu
Normal file
138
grd-cuda-damage-utils.cu
Normal file
@@ -0,0 +1,138 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2021 Pascal Nowack
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License as
|
||||||
|
* published by the Free Software Foundation; either version 2 of the
|
||||||
|
* License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
|
||||||
|
* 02111-1307, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
extern "C"
|
||||||
|
{
|
||||||
|
__global__ void
|
||||||
|
check_damaged_pixel (uint8_t *damage_array,
|
||||||
|
uint8_t *region_is_damaged,
|
||||||
|
uint32_t *current_data,
|
||||||
|
uint32_t *previous_data,
|
||||||
|
uint32_t damage_array_stride,
|
||||||
|
uint32_t data_width,
|
||||||
|
uint32_t data_height,
|
||||||
|
uint32_t data_stride)
|
||||||
|
{
|
||||||
|
uint32_t data_pos;
|
||||||
|
uint8_t damaged = 0;
|
||||||
|
uint32_t x, y;
|
||||||
|
|
||||||
|
x = blockIdx.x * blockDim.x + threadIdx.x;
|
||||||
|
y = blockIdx.y * blockDim.y + threadIdx.y;
|
||||||
|
|
||||||
|
if (x >= data_width || y >= data_height)
|
||||||
|
return;
|
||||||
|
|
||||||
|
data_pos = y * data_stride + x;
|
||||||
|
if (previous_data[data_pos] != current_data[data_pos])
|
||||||
|
{
|
||||||
|
damaged = 1;
|
||||||
|
*region_is_damaged = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
damage_array[y * damage_array_stride + x] = damaged;
|
||||||
|
}
|
||||||
|
|
||||||
|
__global__ void
|
||||||
|
combine_damage_array_cols (uint8_t *damage_array,
|
||||||
|
uint32_t damage_array_width,
|
||||||
|
uint32_t damage_array_height,
|
||||||
|
uint32_t damage_array_stride,
|
||||||
|
uint32_t combine_shift)
|
||||||
|
{
|
||||||
|
uint32_t data_pos;
|
||||||
|
uint32_t neighbour_offset;
|
||||||
|
uint32_t x, y;
|
||||||
|
uint32_t sx;
|
||||||
|
|
||||||
|
sx = blockIdx.x * blockDim.x + threadIdx.x;
|
||||||
|
y = blockIdx.y * blockDim.y + threadIdx.y;
|
||||||
|
|
||||||
|
x = sx << combine_shift + 1;
|
||||||
|
|
||||||
|
if (x >= damage_array_width || y >= damage_array_height)
|
||||||
|
return;
|
||||||
|
|
||||||
|
neighbour_offset = 1 << combine_shift;
|
||||||
|
if (x + neighbour_offset >= damage_array_width)
|
||||||
|
return;
|
||||||
|
|
||||||
|
data_pos = y * damage_array_stride + x;
|
||||||
|
if (damage_array[data_pos + neighbour_offset])
|
||||||
|
damage_array[data_pos] = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
__global__ void
|
||||||
|
combine_damage_array_rows (uint8_t *damage_array,
|
||||||
|
uint32_t damage_array_width,
|
||||||
|
uint32_t damage_array_height,
|
||||||
|
uint32_t damage_array_stride,
|
||||||
|
uint32_t combine_shift)
|
||||||
|
{
|
||||||
|
uint32_t data_pos;
|
||||||
|
uint32_t neighbour_offset;
|
||||||
|
uint32_t x, y;
|
||||||
|
uint32_t sy;
|
||||||
|
|
||||||
|
x = blockIdx.x * blockDim.x + threadIdx.x;
|
||||||
|
sy = blockIdx.y * blockDim.y + threadIdx.y;
|
||||||
|
|
||||||
|
y = sy << combine_shift + 1;
|
||||||
|
|
||||||
|
if (x >= damage_array_width || y >= damage_array_height)
|
||||||
|
return;
|
||||||
|
|
||||||
|
neighbour_offset = 1 << combine_shift;
|
||||||
|
if (y + neighbour_offset >= damage_array_height)
|
||||||
|
return;
|
||||||
|
|
||||||
|
data_pos = y * damage_array_stride + x;
|
||||||
|
if (damage_array[data_pos + neighbour_offset * damage_array_stride])
|
||||||
|
damage_array[data_pos] = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
__global__ void
|
||||||
|
simplify_damage_array (uint8_t *dst_damage_array,
|
||||||
|
uint8_t *src_damage_array,
|
||||||
|
uint32_t dst_damage_array_stride,
|
||||||
|
uint32_t src_damage_array_width,
|
||||||
|
uint32_t src_damage_array_height,
|
||||||
|
uint32_t src_damage_array_stride)
|
||||||
|
{
|
||||||
|
uint32_t src_data_pos, dst_data_pos;
|
||||||
|
uint32_t sx, sy;
|
||||||
|
uint32_t x, y;
|
||||||
|
|
||||||
|
sx = blockIdx.x * blockDim.x + threadIdx.x;
|
||||||
|
sy = blockIdx.y * blockDim.y + threadIdx.y;
|
||||||
|
|
||||||
|
x = sx << 6;
|
||||||
|
y = sy << 6;
|
||||||
|
|
||||||
|
if (x >= src_damage_array_width || y >= src_damage_array_height)
|
||||||
|
return;
|
||||||
|
|
||||||
|
src_data_pos = y * src_damage_array_stride + x;
|
||||||
|
dst_data_pos = sy * dst_damage_array_stride + sx;
|
||||||
|
|
||||||
|
dst_damage_array[dst_data_pos] = src_damage_array[src_data_pos];
|
||||||
|
}
|
||||||
|
}
|
||||||
911
grd-daemon-handover.c
Normal file
911
grd-daemon-handover.c
Normal file
@@ -0,0 +1,911 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2023 SUSE Software Solutions Germany GmbH
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License as
|
||||||
|
* published by the Free Software Foundation; either version 2 of the
|
||||||
|
* License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
|
||||||
|
* 02111-1307, USA.
|
||||||
|
*
|
||||||
|
* Written by:
|
||||||
|
* Joan Torres <joan.torres@suse.com>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
|
#include "grd-daemon-handover.h"
|
||||||
|
|
||||||
|
#include <gio/gunixfdlist.h>
|
||||||
|
#include <glib/gi18n.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include "grd-context.h"
|
||||||
|
#include "grd-daemon.h"
|
||||||
|
#include "grd-daemon-utils.h"
|
||||||
|
#include "grd-dbus-remote-desktop.h"
|
||||||
|
#include "grd-private.h"
|
||||||
|
#include "grd-prompt.h"
|
||||||
|
#include "grd-rdp-server.h"
|
||||||
|
#include "grd-session-rdp.h"
|
||||||
|
#include "grd-settings.h"
|
||||||
|
#include "grd-shell-dialog.h"
|
||||||
|
|
||||||
|
struct _GrdDaemonHandover
|
||||||
|
{
|
||||||
|
GrdDaemon parent;
|
||||||
|
|
||||||
|
GrdDBusRemoteDesktopRdpDispatcher *remote_desktop_dispatcher;
|
||||||
|
GrdDBusRemoteDesktopRdpHandover *remote_desktop_handover;
|
||||||
|
|
||||||
|
GDBusObjectManager *handover_object_manager;
|
||||||
|
|
||||||
|
GrdSession *session;
|
||||||
|
|
||||||
|
gboolean use_system_credentials;
|
||||||
|
GrdPrompt *prompt;
|
||||||
|
GCancellable *prompt_cancellable;
|
||||||
|
|
||||||
|
GrdShellDialog *dialog;
|
||||||
|
|
||||||
|
unsigned int gnome_remote_desktop_watch_name_id;
|
||||||
|
};
|
||||||
|
|
||||||
|
G_DEFINE_TYPE (GrdDaemonHandover, grd_daemon_handover, GRD_TYPE_DAEMON)
|
||||||
|
|
||||||
|
static void
|
||||||
|
on_remote_desktop_rdp_dispatcher_handover_requested (GObject *object,
|
||||||
|
GAsyncResult *result,
|
||||||
|
gpointer user_data);
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
grd_daemon_handover_is_ready (GrdDaemon *daemon)
|
||||||
|
{
|
||||||
|
GrdContext *context = grd_daemon_get_context (daemon);
|
||||||
|
|
||||||
|
if (!grd_context_get_mutter_remote_desktop_proxy (context) ||
|
||||||
|
!grd_context_get_mutter_screen_cast_proxy (context) ||
|
||||||
|
!GRD_DAEMON_HANDOVER (daemon)->remote_desktop_handover)
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
on_take_client_finished (GObject *object,
|
||||||
|
GAsyncResult *result,
|
||||||
|
gpointer user_data)
|
||||||
|
{
|
||||||
|
int fd;
|
||||||
|
int fd_idx = -1;
|
||||||
|
g_autoptr (GError) error = NULL;
|
||||||
|
g_autoptr (GSocket) socket = NULL;
|
||||||
|
g_autoptr (GVariant) fd_variant = NULL;
|
||||||
|
g_autoptr (GUnixFDList) fd_list = NULL;
|
||||||
|
GrdDBusRemoteDesktopRdpHandover *proxy;
|
||||||
|
GrdDaemonHandover *daemon_handover;
|
||||||
|
GrdRdpServer *rdp_server;
|
||||||
|
GSocketConnection *socket_connection;
|
||||||
|
|
||||||
|
proxy = GRD_DBUS_REMOTE_DESKTOP_RDP_HANDOVER (object);
|
||||||
|
if (!grd_dbus_remote_desktop_rdp_handover_call_take_client_finish (
|
||||||
|
proxy, &fd_variant, &fd_list, result, &error))
|
||||||
|
{
|
||||||
|
if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
|
||||||
|
return;
|
||||||
|
|
||||||
|
g_warning ("[DaemonHandover] An error occurred while calling "
|
||||||
|
"TakeClient: %s", error->message);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
g_variant_get (fd_variant, "h", &fd_idx);
|
||||||
|
if (!G_IS_UNIX_FD_LIST (fd_list) ||
|
||||||
|
fd_idx < 0 || fd_idx >= g_unix_fd_list_get_length (fd_list))
|
||||||
|
{
|
||||||
|
g_warning ("Failed to acquire file descriptor: The fd list or fd index "
|
||||||
|
"is invalid");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
fd = g_unix_fd_list_get (fd_list, fd_idx, &error);
|
||||||
|
if (fd == -1)
|
||||||
|
{
|
||||||
|
g_warning ("[DaemonHandover] Failed to acquire file descriptor: %s",
|
||||||
|
error->message);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
socket = g_socket_new_from_fd (fd, &error);
|
||||||
|
if (!socket)
|
||||||
|
{
|
||||||
|
g_warning ("[DaemonHandover] Failed to create socket from fd: %s",
|
||||||
|
error->message);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
daemon_handover = GRD_DAEMON_HANDOVER (user_data);
|
||||||
|
rdp_server = grd_daemon_get_rdp_server (GRD_DAEMON (daemon_handover));
|
||||||
|
socket_connection = g_socket_connection_factory_create_connection (socket);
|
||||||
|
|
||||||
|
grd_rdp_server_notify_incoming (G_SOCKET_SERVICE (rdp_server),
|
||||||
|
socket_connection);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
on_get_system_credentials_finished (GObject *object,
|
||||||
|
GAsyncResult *result,
|
||||||
|
gpointer user_data)
|
||||||
|
{
|
||||||
|
GrdDBusRemoteDesktopRdpHandover *remote_desktop_handover;
|
||||||
|
g_autoptr (GError) error = NULL;
|
||||||
|
g_autofree char *username = NULL;
|
||||||
|
g_autofree char *password = NULL;
|
||||||
|
GrdDaemonHandover *daemon_handover;
|
||||||
|
GrdContext *context;
|
||||||
|
GrdSettings *settings;
|
||||||
|
GCancellable *cancellable;
|
||||||
|
const char* object_path;
|
||||||
|
|
||||||
|
remote_desktop_handover = GRD_DBUS_REMOTE_DESKTOP_RDP_HANDOVER (object);
|
||||||
|
if (!grd_dbus_remote_desktop_rdp_handover_call_get_system_credentials_finish (
|
||||||
|
remote_desktop_handover,
|
||||||
|
&username,
|
||||||
|
&password,
|
||||||
|
result,
|
||||||
|
&error))
|
||||||
|
{
|
||||||
|
if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
|
||||||
|
return;
|
||||||
|
|
||||||
|
g_warning ("[DaemonHandover] Failed to get system credentials: %s",
|
||||||
|
error->message);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
daemon_handover = GRD_DAEMON_HANDOVER (user_data);
|
||||||
|
context = grd_daemon_get_context (GRD_DAEMON (daemon_handover));
|
||||||
|
settings = grd_context_get_settings (context);
|
||||||
|
|
||||||
|
if (!grd_settings_set_rdp_credentials (settings, username, password, &error))
|
||||||
|
{
|
||||||
|
g_warning ("[DaemonHanodver] Failed to overwrite credentials: %s",
|
||||||
|
error->message);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
object_path =
|
||||||
|
g_dbus_proxy_get_object_path (G_DBUS_PROXY (remote_desktop_handover));
|
||||||
|
|
||||||
|
g_debug ("[DaemonHandover] At: %s, calling TakeClient", object_path);
|
||||||
|
|
||||||
|
cancellable = grd_daemon_get_cancellable (GRD_DAEMON (daemon_handover));
|
||||||
|
grd_dbus_remote_desktop_rdp_handover_call_take_client (
|
||||||
|
remote_desktop_handover,
|
||||||
|
NULL,
|
||||||
|
cancellable,
|
||||||
|
on_take_client_finished,
|
||||||
|
daemon_handover);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
on_take_client_ready (GrdDBusRemoteDesktopRdpHandover *interface,
|
||||||
|
gboolean use_system_credentials,
|
||||||
|
GrdDaemonHandover *daemon_handover)
|
||||||
|
{
|
||||||
|
GCancellable *cancellable =
|
||||||
|
grd_daemon_get_cancellable (GRD_DAEMON (daemon_handover));
|
||||||
|
const char* object_path;
|
||||||
|
|
||||||
|
object_path = g_dbus_proxy_get_object_path (G_DBUS_PROXY (interface));
|
||||||
|
|
||||||
|
g_debug ("[DaemonHandover] At: %s, received TakeClientReady signal",
|
||||||
|
object_path);
|
||||||
|
|
||||||
|
daemon_handover->use_system_credentials = use_system_credentials;
|
||||||
|
|
||||||
|
if (use_system_credentials)
|
||||||
|
{
|
||||||
|
g_debug ("[DaemonHandover] At: %s, calling GetSystemCredentials",
|
||||||
|
object_path);
|
||||||
|
|
||||||
|
grd_dbus_remote_desktop_rdp_handover_call_get_system_credentials (
|
||||||
|
interface,
|
||||||
|
cancellable,
|
||||||
|
on_get_system_credentials_finished,
|
||||||
|
daemon_handover);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
g_debug ("[DaemonHandover] At: %s, calling TakeClient", object_path);
|
||||||
|
|
||||||
|
grd_dbus_remote_desktop_rdp_handover_call_take_client (
|
||||||
|
interface,
|
||||||
|
NULL,
|
||||||
|
cancellable,
|
||||||
|
on_take_client_finished,
|
||||||
|
daemon_handover);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
on_start_handover_finished (GObject *object,
|
||||||
|
GAsyncResult *result,
|
||||||
|
gpointer user_data)
|
||||||
|
{
|
||||||
|
GrdDBusRemoteDesktopRdpHandover *proxy;
|
||||||
|
g_autofree char *certificate = NULL;
|
||||||
|
g_autofree char *key = NULL;
|
||||||
|
g_autoptr (GError) error = NULL;
|
||||||
|
GrdDaemonHandover *daemon_handover;
|
||||||
|
GrdContext *context;
|
||||||
|
GrdSettings *settings;
|
||||||
|
|
||||||
|
proxy = GRD_DBUS_REMOTE_DESKTOP_RDP_HANDOVER (object);
|
||||||
|
if (!grd_dbus_remote_desktop_rdp_handover_call_start_handover_finish (
|
||||||
|
proxy, &certificate, &key, result, &error))
|
||||||
|
{
|
||||||
|
if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
|
||||||
|
return;
|
||||||
|
|
||||||
|
g_warning ("[DaemonHandover] Failed to start handover: %s",
|
||||||
|
error->message);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
daemon_handover = GRD_DAEMON_HANDOVER (user_data);
|
||||||
|
context = grd_daemon_get_context (GRD_DAEMON (daemon_handover));
|
||||||
|
settings = grd_context_get_settings (context);
|
||||||
|
|
||||||
|
g_object_set (G_OBJECT (settings),
|
||||||
|
"rdp-server-cert", certificate,
|
||||||
|
"rdp-server-key", key,
|
||||||
|
NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
start_handover (GrdDaemonHandover *daemon_handover)
|
||||||
|
{
|
||||||
|
GrdDaemon *daemon = GRD_DAEMON (daemon_handover);
|
||||||
|
GrdContext *context = grd_daemon_get_context (daemon);
|
||||||
|
GrdSettings *settings = grd_context_get_settings (context);
|
||||||
|
GCancellable *cancellable = grd_daemon_get_cancellable (daemon);
|
||||||
|
g_autofree char *username = NULL;
|
||||||
|
g_autofree char *password = NULL;
|
||||||
|
const char *object_path;
|
||||||
|
|
||||||
|
if (!grd_settings_get_rdp_credentials (settings,
|
||||||
|
&username, &password,
|
||||||
|
NULL))
|
||||||
|
g_assert_not_reached ();
|
||||||
|
|
||||||
|
object_path = g_dbus_proxy_get_object_path (
|
||||||
|
G_DBUS_PROXY (daemon_handover->remote_desktop_handover));
|
||||||
|
|
||||||
|
g_debug ("[DaemonHandover] At: %s, calling StartHandover", object_path);
|
||||||
|
|
||||||
|
grd_dbus_remote_desktop_rdp_handover_call_start_handover (
|
||||||
|
daemon_handover->remote_desktop_handover,
|
||||||
|
username,
|
||||||
|
password,
|
||||||
|
cancellable,
|
||||||
|
on_start_handover_finished,
|
||||||
|
daemon_handover);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
on_session_stopped (GrdSession *session,
|
||||||
|
GrdDaemonHandover *daemon_handover)
|
||||||
|
{
|
||||||
|
if (grd_is_remote_login ())
|
||||||
|
grd_session_manager_call_logout_sync ();
|
||||||
|
|
||||||
|
daemon_handover->session = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
show_insecure_connection_dialog (GrdDaemonHandover *daemon_handover)
|
||||||
|
{
|
||||||
|
GCancellable *cancellable =
|
||||||
|
grd_daemon_get_cancellable (GRD_DAEMON (daemon_handover));
|
||||||
|
|
||||||
|
g_assert (!daemon_handover->dialog);
|
||||||
|
|
||||||
|
daemon_handover->dialog = grd_shell_dialog_new (cancellable);
|
||||||
|
if (!daemon_handover->dialog)
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
g_signal_connect_swapped (daemon_handover->dialog, "cancelled",
|
||||||
|
G_CALLBACK (grd_session_stop),
|
||||||
|
daemon_handover->session);
|
||||||
|
|
||||||
|
grd_shell_dialog_open (daemon_handover->dialog,
|
||||||
|
_("Continue With Insecure Connection?"),
|
||||||
|
/* Translators: Don't translate “use redirection server name:i:1”.
|
||||||
|
* It's a menu option, and it's the same for all languages. */
|
||||||
|
_("This Remote Desktop connection is insecure. "
|
||||||
|
"To secure this connection, enable RDSTLS Security "
|
||||||
|
"in your client by saving the connection settings "
|
||||||
|
"in your client as an RDP file and set "
|
||||||
|
"“use redirection server name:i:1” in it."),
|
||||||
|
_("Disconnect"),
|
||||||
|
_("Continue"));
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
prompt_response_callback (GObject *source_object,
|
||||||
|
GAsyncResult *async_result,
|
||||||
|
gpointer user_data)
|
||||||
|
{
|
||||||
|
GrdDaemonHandover *daemon_handover;
|
||||||
|
g_autoptr (GError) error = NULL;
|
||||||
|
GrdPromptResponse response;
|
||||||
|
|
||||||
|
if (!grd_prompt_query_finish (GRD_PROMPT (source_object),
|
||||||
|
async_result,
|
||||||
|
&response,
|
||||||
|
&error))
|
||||||
|
{
|
||||||
|
if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
|
||||||
|
{
|
||||||
|
daemon_handover = GRD_DAEMON_HANDOVER (user_data);
|
||||||
|
|
||||||
|
g_warning ("Failed to query user about session: %s", error->message);
|
||||||
|
grd_session_stop (daemon_handover->session);
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
daemon_handover = GRD_DAEMON_HANDOVER (user_data);
|
||||||
|
|
||||||
|
switch (response)
|
||||||
|
{
|
||||||
|
case GRD_PROMPT_RESPONSE_ACCEPT:
|
||||||
|
return;
|
||||||
|
case GRD_PROMPT_RESPONSE_CANCEL:
|
||||||
|
grd_session_stop (daemon_handover->session);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
g_assert_not_reached ();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
show_insecure_connection_prompt (GrdDaemonHandover *daemon_handover)
|
||||||
|
{
|
||||||
|
g_autoptr (GrdPromptDefinition) prompt_definition = NULL;
|
||||||
|
|
||||||
|
g_assert (!daemon_handover->prompt);
|
||||||
|
|
||||||
|
daemon_handover->prompt = g_object_new (GRD_TYPE_PROMPT, NULL);
|
||||||
|
daemon_handover->prompt_cancellable = g_cancellable_new ();
|
||||||
|
|
||||||
|
prompt_definition = g_new0 (GrdPromptDefinition, 1);
|
||||||
|
prompt_definition->summary =
|
||||||
|
g_strdup_printf (_("Continue With Insecure Connection?"));
|
||||||
|
prompt_definition->body =
|
||||||
|
/* Translators: Don't translate “use redirection server name:i:1”.
|
||||||
|
* It's a menu option, and it's the same for all languages. */
|
||||||
|
g_strdup_printf (_("This Remote Desktop connection is insecure. "
|
||||||
|
"To secure this connection, enable RDSTLS Security "
|
||||||
|
"in your client by saving the connection settings "
|
||||||
|
"in your client as an RDP file and set "
|
||||||
|
"“use redirection server name:i:1” in it."));
|
||||||
|
prompt_definition->cancel_label = g_strdup_printf (_("Disconnect"));
|
||||||
|
prompt_definition->accept_label = g_strdup_printf (_("Continue"));
|
||||||
|
|
||||||
|
grd_prompt_query_async (daemon_handover->prompt,
|
||||||
|
prompt_definition,
|
||||||
|
daemon_handover->prompt_cancellable,
|
||||||
|
prompt_response_callback,
|
||||||
|
daemon_handover);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
inform_about_insecure_connection (GrdDaemonHandover *daemon_handover)
|
||||||
|
{
|
||||||
|
gboolean could_display_dialog;
|
||||||
|
|
||||||
|
could_display_dialog = show_insecure_connection_dialog (daemon_handover);
|
||||||
|
if (!could_display_dialog)
|
||||||
|
show_insecure_connection_prompt (daemon_handover);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
on_redirect_client (GrdDBusRemoteDesktopRdpHandover *interface,
|
||||||
|
const char *routing_token,
|
||||||
|
const char *username,
|
||||||
|
const char *password,
|
||||||
|
GrdDaemonHandover *daemon_handover)
|
||||||
|
{
|
||||||
|
const char *object_path =
|
||||||
|
g_dbus_proxy_get_object_path (G_DBUS_PROXY (interface));
|
||||||
|
GrdContext *context = grd_daemon_get_context (GRD_DAEMON (daemon_handover));
|
||||||
|
GrdSettings *settings = grd_context_get_settings (context);
|
||||||
|
GrdSessionRdp *session_rdp = GRD_SESSION_RDP (daemon_handover->session);
|
||||||
|
g_autofree char *certificate = NULL;
|
||||||
|
|
||||||
|
g_debug ("[DaemonHandover] At: %s, received RedirectClient signal",
|
||||||
|
object_path);
|
||||||
|
|
||||||
|
g_object_get (G_OBJECT (settings),
|
||||||
|
"rdp-server-cert", &certificate,
|
||||||
|
NULL);
|
||||||
|
|
||||||
|
if (!grd_session_rdp_send_server_redirection (session_rdp, routing_token,
|
||||||
|
username, password,
|
||||||
|
certificate))
|
||||||
|
grd_session_stop (daemon_handover->session);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
on_handover_is_waiting_changed (GrdDBusRemoteDesktopRdpHandover *proxy,
|
||||||
|
GParamSpec *pspec,
|
||||||
|
GrdDaemonHandover *daemon_handover)
|
||||||
|
{
|
||||||
|
gboolean handover_is_waiting;
|
||||||
|
|
||||||
|
handover_is_waiting =
|
||||||
|
grd_dbus_remote_desktop_rdp_handover_get_handover_is_waiting (
|
||||||
|
daemon_handover->remote_desktop_handover);
|
||||||
|
|
||||||
|
if (!handover_is_waiting)
|
||||||
|
return;
|
||||||
|
|
||||||
|
start_handover (daemon_handover);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
on_incoming_new_connection (GrdRdpServer *rdp_server,
|
||||||
|
GrdSession *session,
|
||||||
|
GrdDaemonHandover *daemon_handover)
|
||||||
|
{
|
||||||
|
GrdContext *context = grd_daemon_get_context (GRD_DAEMON (daemon_handover));
|
||||||
|
GrdSettings *settings = grd_context_get_settings (context);
|
||||||
|
|
||||||
|
if (daemon_handover->session)
|
||||||
|
{
|
||||||
|
g_signal_handlers_disconnect_by_func (daemon_handover->session,
|
||||||
|
G_CALLBACK (on_session_stopped),
|
||||||
|
daemon_handover);
|
||||||
|
grd_session_stop (daemon_handover->session);
|
||||||
|
}
|
||||||
|
|
||||||
|
g_signal_connect (session, "stopped",
|
||||||
|
G_CALLBACK (on_session_stopped),
|
||||||
|
daemon_handover);
|
||||||
|
|
||||||
|
daemon_handover->session = session;
|
||||||
|
|
||||||
|
grd_settings_recreate_rdp_credentials (settings);
|
||||||
|
|
||||||
|
if (daemon_handover->use_system_credentials && grd_is_remote_login ())
|
||||||
|
inform_about_insecure_connection (daemon_handover);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
setup_handover (GrdDaemonHandover *daemon_handover)
|
||||||
|
{
|
||||||
|
GrdRdpServer *rdp_server;
|
||||||
|
gboolean handover_is_waiting;
|
||||||
|
|
||||||
|
if (!daemon_handover->remote_desktop_handover)
|
||||||
|
return;
|
||||||
|
|
||||||
|
rdp_server = grd_daemon_get_rdp_server (GRD_DAEMON (daemon_handover));
|
||||||
|
if (!rdp_server)
|
||||||
|
return;
|
||||||
|
|
||||||
|
g_signal_connect (daemon_handover->remote_desktop_handover, "take-client-ready",
|
||||||
|
G_CALLBACK (on_take_client_ready), daemon_handover);
|
||||||
|
g_signal_connect (daemon_handover->remote_desktop_handover, "redirect-client",
|
||||||
|
G_CALLBACK (on_redirect_client), daemon_handover);
|
||||||
|
g_signal_connect (daemon_handover->remote_desktop_handover, "notify::handover-is-waiting",
|
||||||
|
G_CALLBACK (on_handover_is_waiting_changed), daemon_handover);
|
||||||
|
|
||||||
|
g_signal_connect (rdp_server, "incoming-new-connection",
|
||||||
|
G_CALLBACK (on_incoming_new_connection), daemon_handover);
|
||||||
|
|
||||||
|
handover_is_waiting = grd_dbus_remote_desktop_rdp_handover_get_handover_is_waiting (
|
||||||
|
daemon_handover->remote_desktop_handover);
|
||||||
|
|
||||||
|
if (handover_is_waiting)
|
||||||
|
start_handover (daemon_handover);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
teardown_handover (GrdDaemonHandover *daemon_handover)
|
||||||
|
{
|
||||||
|
GrdRdpServer *rdp_server =
|
||||||
|
grd_daemon_get_rdp_server (GRD_DAEMON (daemon_handover));
|
||||||
|
|
||||||
|
if (daemon_handover->remote_desktop_handover)
|
||||||
|
{
|
||||||
|
g_signal_handlers_disconnect_by_func (daemon_handover->remote_desktop_handover,
|
||||||
|
G_CALLBACK (on_take_client_ready),
|
||||||
|
daemon_handover);
|
||||||
|
g_signal_handlers_disconnect_by_func (daemon_handover->remote_desktop_handover,
|
||||||
|
G_CALLBACK (on_redirect_client),
|
||||||
|
daemon_handover);
|
||||||
|
g_signal_handlers_disconnect_by_func (daemon_handover->remote_desktop_handover,
|
||||||
|
G_CALLBACK (on_handover_is_waiting_changed),
|
||||||
|
daemon_handover);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rdp_server)
|
||||||
|
{
|
||||||
|
g_signal_handlers_disconnect_by_func (rdp_server,
|
||||||
|
G_CALLBACK (on_incoming_new_connection),
|
||||||
|
daemon_handover);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
on_rdp_server_started (GrdDaemonHandover *daemon_handover)
|
||||||
|
{
|
||||||
|
setup_handover (daemon_handover);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
on_rdp_server_stopped (GrdDaemonHandover *daemon_handover)
|
||||||
|
{
|
||||||
|
teardown_handover (daemon_handover);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
on_handover_object_added (GDBusObjectManager *manager,
|
||||||
|
GDBusObject *object,
|
||||||
|
GrdDaemonHandover *daemon_handover)
|
||||||
|
{
|
||||||
|
g_autofree char *session_id = NULL;
|
||||||
|
g_autofree char *expected_object_path = NULL;
|
||||||
|
const char *object_path;
|
||||||
|
GCancellable *cancellable;
|
||||||
|
|
||||||
|
session_id = grd_get_session_id_from_pid (getpid ());
|
||||||
|
if (!session_id)
|
||||||
|
{
|
||||||
|
g_warning ("[DaemonHandover] Could not get session id");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
expected_object_path = g_strdup_printf ("%s/session%s",
|
||||||
|
REMOTE_DESKTOP_HANDOVERS_OBJECT_PATH,
|
||||||
|
session_id);
|
||||||
|
object_path = g_dbus_object_get_object_path (object);
|
||||||
|
|
||||||
|
if (g_strcmp0 (object_path, expected_object_path) != 0)
|
||||||
|
{
|
||||||
|
g_debug ("[DaemonHandover] Ignoring handover object at: %s, "
|
||||||
|
"expected: %s", object_path, expected_object_path);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
cancellable = grd_daemon_get_cancellable (GRD_DAEMON (daemon_handover));
|
||||||
|
grd_dbus_remote_desktop_rdp_dispatcher_call_request_handover (
|
||||||
|
daemon_handover->remote_desktop_dispatcher,
|
||||||
|
cancellable,
|
||||||
|
on_remote_desktop_rdp_dispatcher_handover_requested,
|
||||||
|
daemon_handover);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
on_handover_object_manager_acquired (GObject *source_object,
|
||||||
|
GAsyncResult *result,
|
||||||
|
gpointer user_data)
|
||||||
|
{
|
||||||
|
g_autoptr (GDBusObjectManager) manager = NULL;
|
||||||
|
g_autoptr (GError) error = NULL;
|
||||||
|
GrdDaemonHandover *daemon_handover;
|
||||||
|
|
||||||
|
manager =
|
||||||
|
grd_dbus_remote_desktop_object_manager_client_new_for_bus_finish (result,
|
||||||
|
&error);
|
||||||
|
if (!manager)
|
||||||
|
{
|
||||||
|
if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
|
||||||
|
g_warning ("[DaemonHandover] Failed to create handover object manager: %s",
|
||||||
|
error->message);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
daemon_handover = GRD_DAEMON_HANDOVER (user_data);
|
||||||
|
daemon_handover->handover_object_manager = g_steal_pointer (&manager);
|
||||||
|
|
||||||
|
g_debug ("[DaemonHandover] Watching handover objects");
|
||||||
|
|
||||||
|
g_signal_connect (daemon_handover->handover_object_manager, "object-added",
|
||||||
|
G_CALLBACK (on_handover_object_added), daemon_handover);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
start_watching_handover_objects (GrdDaemonHandover *daemon_handover)
|
||||||
|
{
|
||||||
|
GCancellable *cancellable;
|
||||||
|
|
||||||
|
if (daemon_handover->handover_object_manager)
|
||||||
|
return;
|
||||||
|
|
||||||
|
cancellable = grd_daemon_get_cancellable (GRD_DAEMON (daemon_handover));
|
||||||
|
|
||||||
|
grd_dbus_remote_desktop_object_manager_client_new_for_bus (
|
||||||
|
G_BUS_TYPE_SYSTEM,
|
||||||
|
G_DBUS_OBJECT_MANAGER_CLIENT_FLAGS_DO_NOT_AUTO_START,
|
||||||
|
REMOTE_DESKTOP_BUS_NAME,
|
||||||
|
REMOTE_DESKTOP_HANDOVERS_OBJECT_PATH,
|
||||||
|
cancellable,
|
||||||
|
on_handover_object_manager_acquired,
|
||||||
|
daemon_handover);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
stop_watching_handover_objects (GrdDaemonHandover *daemon_handover)
|
||||||
|
{
|
||||||
|
if (!daemon_handover->handover_object_manager)
|
||||||
|
return;
|
||||||
|
|
||||||
|
g_signal_handlers_disconnect_by_func (daemon_handover->handover_object_manager,
|
||||||
|
G_CALLBACK (on_handover_object_added),
|
||||||
|
daemon_handover);
|
||||||
|
|
||||||
|
g_clear_object (&daemon_handover->handover_object_manager);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
on_remote_desktop_rdp_handover_proxy_acquired (GObject *object,
|
||||||
|
GAsyncResult *result,
|
||||||
|
gpointer user_data)
|
||||||
|
{
|
||||||
|
GrdDaemonHandover *daemon_handover;
|
||||||
|
g_autoptr (GrdDBusRemoteDesktopRdpHandover) proxy = NULL;
|
||||||
|
g_autoptr (GError) error = NULL;
|
||||||
|
|
||||||
|
proxy =
|
||||||
|
grd_dbus_remote_desktop_rdp_handover_proxy_new_for_bus_finish (result,
|
||||||
|
&error);
|
||||||
|
if (!proxy)
|
||||||
|
{
|
||||||
|
if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
|
||||||
|
return;
|
||||||
|
|
||||||
|
g_warning ("[DaemonHandover] Failed to create remote desktop handover "
|
||||||
|
"proxy: %s", error->message);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
daemon_handover = GRD_DAEMON_HANDOVER (user_data);
|
||||||
|
|
||||||
|
stop_watching_handover_objects (daemon_handover);
|
||||||
|
|
||||||
|
daemon_handover->remote_desktop_handover = g_steal_pointer (&proxy);
|
||||||
|
|
||||||
|
setup_handover (daemon_handover);
|
||||||
|
|
||||||
|
grd_daemon_maybe_enable_services (GRD_DAEMON (daemon_handover));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
on_remote_desktop_rdp_dispatcher_handover_requested (GObject *object,
|
||||||
|
GAsyncResult *result,
|
||||||
|
gpointer user_data)
|
||||||
|
{
|
||||||
|
GrdDBusRemoteDesktopRdpDispatcher *remote_desktop_dispatcher;
|
||||||
|
g_autofree char *object_path = NULL;
|
||||||
|
g_autoptr (GError) error = NULL;
|
||||||
|
GrdDaemonHandover *daemon_handover;
|
||||||
|
GCancellable *cancellable;
|
||||||
|
gboolean success;
|
||||||
|
|
||||||
|
remote_desktop_dispatcher = GRD_DBUS_REMOTE_DESKTOP_RDP_DISPATCHER (object);
|
||||||
|
success =
|
||||||
|
grd_dbus_remote_desktop_rdp_dispatcher_call_request_handover_finish (
|
||||||
|
remote_desktop_dispatcher,
|
||||||
|
&object_path,
|
||||||
|
result,
|
||||||
|
&error);
|
||||||
|
if (!success)
|
||||||
|
{
|
||||||
|
if (g_error_matches (error, GRD_DBUS_ERROR, GRD_DBUS_ERROR_NO_HANDOVER))
|
||||||
|
{
|
||||||
|
daemon_handover = GRD_DAEMON_HANDOVER (user_data);
|
||||||
|
start_watching_handover_objects (daemon_handover);
|
||||||
|
}
|
||||||
|
else if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
|
||||||
|
{
|
||||||
|
g_warning ("[DaemonHandover] Failed to request remote desktop "
|
||||||
|
"handover: %s", error->message);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
g_debug ("[DaemonHandover] Using: %s, from dispatcher request",
|
||||||
|
object_path);
|
||||||
|
|
||||||
|
daemon_handover = GRD_DAEMON_HANDOVER (user_data);
|
||||||
|
cancellable = grd_daemon_get_cancellable (GRD_DAEMON (daemon_handover));
|
||||||
|
|
||||||
|
grd_dbus_remote_desktop_rdp_handover_proxy_new_for_bus (
|
||||||
|
G_BUS_TYPE_SYSTEM,
|
||||||
|
G_DBUS_PROXY_FLAGS_NONE,
|
||||||
|
REMOTE_DESKTOP_BUS_NAME,
|
||||||
|
object_path,
|
||||||
|
cancellable,
|
||||||
|
on_remote_desktop_rdp_handover_proxy_acquired,
|
||||||
|
daemon_handover);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
on_remote_desktop_rdp_dispatcher_proxy_acquired (GObject *object,
|
||||||
|
GAsyncResult *result,
|
||||||
|
gpointer user_data)
|
||||||
|
{
|
||||||
|
g_autoptr (GrdDBusRemoteDesktopRdpDispatcher) proxy = NULL;
|
||||||
|
g_autoptr (GError) error = NULL;
|
||||||
|
GrdDaemonHandover *daemon_handover;
|
||||||
|
GCancellable *cancellable;
|
||||||
|
|
||||||
|
proxy =
|
||||||
|
grd_dbus_remote_desktop_rdp_dispatcher_proxy_new_finish (result, &error);
|
||||||
|
if (!proxy)
|
||||||
|
{
|
||||||
|
if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
|
||||||
|
return;
|
||||||
|
|
||||||
|
g_warning ("[DaemonHandover] Failed to create remote desktop "
|
||||||
|
"dispatcher proxy: %s", error->message);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
daemon_handover = GRD_DAEMON_HANDOVER (user_data);
|
||||||
|
|
||||||
|
g_assert (!daemon_handover->remote_desktop_dispatcher);
|
||||||
|
daemon_handover->remote_desktop_dispatcher = g_steal_pointer (&proxy);
|
||||||
|
|
||||||
|
cancellable = grd_daemon_get_cancellable (GRD_DAEMON (daemon_handover));
|
||||||
|
grd_dbus_remote_desktop_rdp_dispatcher_call_request_handover (
|
||||||
|
daemon_handover->remote_desktop_dispatcher,
|
||||||
|
cancellable,
|
||||||
|
on_remote_desktop_rdp_dispatcher_handover_requested,
|
||||||
|
daemon_handover);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
on_gnome_remote_desktop_name_appeared (GDBusConnection *connection,
|
||||||
|
const char *name,
|
||||||
|
const char *name_owner,
|
||||||
|
gpointer user_data)
|
||||||
|
{
|
||||||
|
GrdDaemonHandover *daemon_handover = user_data;
|
||||||
|
GCancellable *cancellable =
|
||||||
|
grd_daemon_get_cancellable (GRD_DAEMON (daemon_handover));
|
||||||
|
|
||||||
|
g_debug ("[DaemonHandover] %s name appeared", name);
|
||||||
|
|
||||||
|
grd_dbus_remote_desktop_rdp_dispatcher_proxy_new (
|
||||||
|
connection,
|
||||||
|
G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START,
|
||||||
|
REMOTE_DESKTOP_BUS_NAME,
|
||||||
|
REMOTE_DESKTOP_DISPATCHER_OBJECT_PATH,
|
||||||
|
cancellable,
|
||||||
|
on_remote_desktop_rdp_dispatcher_proxy_acquired,
|
||||||
|
daemon_handover);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
on_gnome_remote_desktop_name_vanished (GDBusConnection *connection,
|
||||||
|
const char *name,
|
||||||
|
gpointer user_data)
|
||||||
|
{
|
||||||
|
GrdDaemonHandover *daemon_handover = user_data;
|
||||||
|
|
||||||
|
g_warning ("[DaemonHandover] %s name vanished", name);
|
||||||
|
|
||||||
|
teardown_handover (daemon_handover);
|
||||||
|
|
||||||
|
g_clear_object (&daemon_handover->remote_desktop_handover);
|
||||||
|
g_clear_object (&daemon_handover->remote_desktop_dispatcher);
|
||||||
|
|
||||||
|
stop_watching_handover_objects (daemon_handover);
|
||||||
|
|
||||||
|
if (grd_is_remote_login ())
|
||||||
|
grd_session_manager_call_logout_sync ();
|
||||||
|
}
|
||||||
|
|
||||||
|
GrdDaemonHandover *
|
||||||
|
grd_daemon_handover_new (GError **error)
|
||||||
|
{
|
||||||
|
GrdContext *context;
|
||||||
|
|
||||||
|
context = grd_context_new (GRD_RUNTIME_MODE_HANDOVER, error);
|
||||||
|
if (!context)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
return g_object_new (GRD_TYPE_DAEMON_HANDOVER,
|
||||||
|
"application-id", GRD_DAEMON_HANDOVER_APPLICATION_ID,
|
||||||
|
"flags", G_APPLICATION_IS_SERVICE,
|
||||||
|
"context", context,
|
||||||
|
NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
grd_daemon_handover_init (GrdDaemonHandover *daemon_handover)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
grd_daemon_handover_startup (GApplication *app)
|
||||||
|
{
|
||||||
|
GrdDaemonHandover *daemon_handover = GRD_DAEMON_HANDOVER (app);
|
||||||
|
|
||||||
|
grd_daemon_acquire_mutter_dbus_proxies (GRD_DAEMON (daemon_handover));
|
||||||
|
|
||||||
|
g_signal_connect (daemon_handover, "mutter-proxy-acquired",
|
||||||
|
G_CALLBACK (grd_daemon_maybe_enable_services), NULL);
|
||||||
|
|
||||||
|
daemon_handover->gnome_remote_desktop_watch_name_id =
|
||||||
|
g_bus_watch_name (G_BUS_TYPE_SYSTEM,
|
||||||
|
REMOTE_DESKTOP_BUS_NAME,
|
||||||
|
G_BUS_NAME_WATCHER_FLAGS_NONE,
|
||||||
|
on_gnome_remote_desktop_name_appeared,
|
||||||
|
on_gnome_remote_desktop_name_vanished,
|
||||||
|
daemon_handover, NULL);
|
||||||
|
|
||||||
|
g_signal_connect (daemon_handover, "rdp-server-started",
|
||||||
|
G_CALLBACK (on_rdp_server_started), NULL);
|
||||||
|
|
||||||
|
g_signal_connect (daemon_handover, "rdp-server-stopped",
|
||||||
|
G_CALLBACK (on_rdp_server_stopped), NULL);
|
||||||
|
|
||||||
|
G_APPLICATION_CLASS (grd_daemon_handover_parent_class)->startup (app);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
grd_daemon_handover_shutdown (GApplication *app)
|
||||||
|
{
|
||||||
|
GrdDaemonHandover *daemon_handover = GRD_DAEMON_HANDOVER (app);
|
||||||
|
GrdDaemon *daemon = GRD_DAEMON (daemon_handover);
|
||||||
|
|
||||||
|
g_cancellable_cancel (grd_daemon_get_cancellable (daemon));
|
||||||
|
|
||||||
|
g_clear_object (&daemon_handover->remote_desktop_handover);
|
||||||
|
g_clear_object (&daemon_handover->remote_desktop_dispatcher);
|
||||||
|
|
||||||
|
stop_watching_handover_objects (daemon_handover);
|
||||||
|
|
||||||
|
g_clear_handle_id (&daemon_handover->gnome_remote_desktop_watch_name_id,
|
||||||
|
g_bus_unwatch_name);
|
||||||
|
|
||||||
|
if (daemon_handover->prompt_cancellable)
|
||||||
|
{
|
||||||
|
g_cancellable_cancel (daemon_handover->prompt_cancellable);
|
||||||
|
g_clear_object (&daemon_handover->prompt_cancellable);
|
||||||
|
}
|
||||||
|
g_clear_object (&daemon_handover->prompt);
|
||||||
|
|
||||||
|
g_clear_object (&daemon_handover->dialog);
|
||||||
|
|
||||||
|
G_APPLICATION_CLASS (grd_daemon_handover_parent_class)->shutdown (app);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
grd_daemon_handover_class_init (GrdDaemonHandoverClass *klass)
|
||||||
|
{
|
||||||
|
GApplicationClass *g_application_class = G_APPLICATION_CLASS (klass);
|
||||||
|
GrdDaemonClass *daemon_class = GRD_DAEMON_CLASS (klass);
|
||||||
|
|
||||||
|
g_application_class->startup = grd_daemon_handover_startup;
|
||||||
|
g_application_class->shutdown = grd_daemon_handover_shutdown;
|
||||||
|
|
||||||
|
daemon_class->is_daemon_ready = grd_daemon_handover_is_ready;
|
||||||
|
}
|
||||||
31
grd-daemon-handover.h
Normal file
31
grd-daemon-handover.h
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2023 SUSE Software Solutions Germany GmbH
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License as
|
||||||
|
* published by the Free Software Foundation; either version 2 of the
|
||||||
|
* License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
|
||||||
|
* 02111-1307, USA.
|
||||||
|
*
|
||||||
|
* Written by:
|
||||||
|
* Joan Torres <joan.torres@suse.com>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "grd-daemon.h"
|
||||||
|
|
||||||
|
#define GRD_TYPE_DAEMON_HANDOVER (grd_daemon_handover_get_type ())
|
||||||
|
G_DECLARE_FINAL_TYPE (GrdDaemonHandover, grd_daemon_handover,
|
||||||
|
GRD, DAEMON_HANDOVER, GrdDaemon)
|
||||||
|
|
||||||
|
GrdDaemonHandover *grd_daemon_handover_new (GError **error);
|
||||||
1421
grd-daemon-system.c
Normal file
1421
grd-daemon-system.c
Normal file
File diff suppressed because it is too large
Load Diff
31
grd-daemon-system.h
Normal file
31
grd-daemon-system.h
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2023 SUSE Software Solutions Germany GmbH
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License as
|
||||||
|
* published by the Free Software Foundation; either version 2 of the
|
||||||
|
* License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
|
||||||
|
* 02111-1307, USA.
|
||||||
|
*
|
||||||
|
* Written by:
|
||||||
|
* Joan Torres <joan.torres@suse.com>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "grd-daemon.h"
|
||||||
|
|
||||||
|
#define GRD_TYPE_DAEMON_SYSTEM (grd_daemon_system_get_type ())
|
||||||
|
G_DECLARE_FINAL_TYPE (GrdDaemonSystem, grd_daemon_system,
|
||||||
|
GRD, DAEMON_SYSTEM, GrdDaemon)
|
||||||
|
|
||||||
|
GrdDaemonSystem *grd_daemon_system_new (GError **error);
|
||||||
291
grd-daemon-user.c
Normal file
291
grd-daemon-user.c
Normal file
@@ -0,0 +1,291 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2022 Red Hat Inc.
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License as
|
||||||
|
* published by the Free Software Foundation; either version 2 of the
|
||||||
|
* License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
|
||||||
|
* 02111-1307, USA.
|
||||||
|
*
|
||||||
|
* Written by:
|
||||||
|
* Jonas Ådahl <jadahl@gmail.com>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
|
#include "grd-daemon-user.h"
|
||||||
|
|
||||||
|
#include <glib/gi18n.h>
|
||||||
|
|
||||||
|
#include "grd-private.h"
|
||||||
|
#include "grd-prompt.h"
|
||||||
|
|
||||||
|
struct _GrdDaemonUser
|
||||||
|
{
|
||||||
|
GrdDaemon parent;
|
||||||
|
|
||||||
|
#ifdef HAVE_RDP
|
||||||
|
GrdDBusRemoteDesktopRdpServer *system_rdp_server;
|
||||||
|
|
||||||
|
GrdPrompt *prompt;
|
||||||
|
GCancellable *prompt_cancellable;
|
||||||
|
#endif /* HAVE_RDP */
|
||||||
|
};
|
||||||
|
|
||||||
|
G_DEFINE_TYPE (GrdDaemonUser, grd_daemon_user, GRD_TYPE_DAEMON)
|
||||||
|
|
||||||
|
#ifdef HAVE_RDP
|
||||||
|
static void
|
||||||
|
on_rdp_server_stopped (GrdDaemonUser *daemon_user)
|
||||||
|
{
|
||||||
|
if (daemon_user->prompt_cancellable)
|
||||||
|
{
|
||||||
|
g_cancellable_cancel (daemon_user->prompt_cancellable);
|
||||||
|
g_clear_object (&daemon_user->prompt_cancellable);
|
||||||
|
}
|
||||||
|
g_clear_object (&daemon_user->prompt);
|
||||||
|
|
||||||
|
g_signal_handlers_disconnect_by_func (daemon_user,
|
||||||
|
G_CALLBACK (on_rdp_server_stopped),
|
||||||
|
NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
prompt_response_callback (GObject *source_object,
|
||||||
|
GAsyncResult *async_result,
|
||||||
|
gpointer user_data)
|
||||||
|
{
|
||||||
|
GrdDaemonUser *daemon_user = GRD_DAEMON_USER (user_data);
|
||||||
|
g_autoptr (GError) error = NULL;
|
||||||
|
GrdPromptResponse response;
|
||||||
|
|
||||||
|
if (!grd_prompt_query_finish (daemon_user->prompt,
|
||||||
|
async_result,
|
||||||
|
&response,
|
||||||
|
&error))
|
||||||
|
{
|
||||||
|
if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
|
||||||
|
{
|
||||||
|
g_warning ("Failed to query user about port change: %s",
|
||||||
|
error->message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
g_clear_object (&daemon_user->prompt);
|
||||||
|
g_clear_object (&daemon_user->prompt_cancellable);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
show_port_changed_prompt (GrdDaemonUser *daemon_user,
|
||||||
|
const int system_rdp_port,
|
||||||
|
const int selected_rdp_port)
|
||||||
|
{
|
||||||
|
g_autoptr (GrdPromptDefinition) prompt_definition = NULL;
|
||||||
|
|
||||||
|
daemon_user->prompt = g_object_new (GRD_TYPE_PROMPT, NULL);
|
||||||
|
daemon_user->prompt_cancellable = g_cancellable_new ();
|
||||||
|
|
||||||
|
prompt_definition = g_new0 (GrdPromptDefinition, 1);
|
||||||
|
prompt_definition->summary =
|
||||||
|
g_strdup_printf (_("Desktop Sharing port changed"));
|
||||||
|
prompt_definition->body =
|
||||||
|
g_strdup_printf (_("The Desktop Sharing port has changed from <b>%i</b> to "
|
||||||
|
"<b>%i</b>.\n"
|
||||||
|
"This is because the Remote Sessions service has been "
|
||||||
|
"activated and it has a higher priority to use port "
|
||||||
|
"<b>%i</b>."),
|
||||||
|
system_rdp_port, selected_rdp_port, system_rdp_port);
|
||||||
|
prompt_definition->accept_label = g_strdup_printf (_("Accept"));
|
||||||
|
|
||||||
|
grd_prompt_query_async (daemon_user->prompt,
|
||||||
|
prompt_definition,
|
||||||
|
daemon_user->prompt_cancellable,
|
||||||
|
prompt_response_callback,
|
||||||
|
daemon_user);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
on_rdp_server_started (GrdDaemonUser *daemon_user)
|
||||||
|
{
|
||||||
|
GrdContext *context = grd_daemon_get_context (GRD_DAEMON (daemon_user));
|
||||||
|
GrdDBusRemoteDesktopRdpServer *rdp_server_iface =
|
||||||
|
grd_context_get_rdp_server_interface (context);
|
||||||
|
int selected_rdp_port =
|
||||||
|
grd_dbus_remote_desktop_rdp_server_get_port (rdp_server_iface);
|
||||||
|
int system_rdp_port =
|
||||||
|
grd_dbus_remote_desktop_rdp_server_get_port (daemon_user->system_rdp_server);
|
||||||
|
|
||||||
|
g_assert (selected_rdp_port != system_rdp_port);
|
||||||
|
|
||||||
|
show_port_changed_prompt (daemon_user, system_rdp_port, selected_rdp_port);
|
||||||
|
|
||||||
|
g_signal_handlers_disconnect_by_func (daemon_user,
|
||||||
|
G_CALLBACK (on_rdp_server_started),
|
||||||
|
NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
on_system_rdp_server_binding (GrdDBusRemoteDesktopRdpServer *system_rdp_server,
|
||||||
|
int system_rdp_port,
|
||||||
|
GrdDaemonUser *daemon_user)
|
||||||
|
{
|
||||||
|
GrdContext *context = grd_daemon_get_context (GRD_DAEMON (daemon_user));
|
||||||
|
GrdDBusRemoteDesktopRdpServer *user_rdp_server =
|
||||||
|
grd_context_get_rdp_server_interface (context);
|
||||||
|
int user_rdp_port =
|
||||||
|
grd_dbus_remote_desktop_rdp_server_get_port (user_rdp_server);
|
||||||
|
|
||||||
|
if (system_rdp_port != user_rdp_port)
|
||||||
|
return;
|
||||||
|
|
||||||
|
g_debug ("[DaemonUser] Restarting RDP server due to port conflict with the "
|
||||||
|
"system daemon");
|
||||||
|
|
||||||
|
g_signal_connect (daemon_user, "rdp-server-stopped",
|
||||||
|
G_CALLBACK (on_rdp_server_stopped), NULL);
|
||||||
|
g_signal_connect (daemon_user, "rdp-server-started",
|
||||||
|
G_CALLBACK (on_rdp_server_started), NULL);
|
||||||
|
|
||||||
|
grd_daemon_restart_rdp_server_with_delay (GRD_DAEMON (daemon_user));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
on_remote_desktop_rdp_server_proxy_acquired (GObject *object,
|
||||||
|
GAsyncResult *result,
|
||||||
|
gpointer user_data)
|
||||||
|
{
|
||||||
|
GrdDaemonUser *daemon_user = user_data;
|
||||||
|
g_autoptr (GError) error = NULL;
|
||||||
|
|
||||||
|
daemon_user->system_rdp_server =
|
||||||
|
grd_dbus_remote_desktop_rdp_server_proxy_new_for_bus_finish (result, &error);
|
||||||
|
if (!daemon_user->system_rdp_server)
|
||||||
|
{
|
||||||
|
g_warning ("[DaemonUser] Failed to create system RDP server proxy: "
|
||||||
|
"%s", error->message);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
g_signal_connect (daemon_user->system_rdp_server, "binding",
|
||||||
|
G_CALLBACK (on_system_rdp_server_binding),
|
||||||
|
daemon_user);
|
||||||
|
}
|
||||||
|
#endif /* HAVE_RDP */
|
||||||
|
|
||||||
|
GrdDaemonUser *
|
||||||
|
grd_daemon_user_new (GrdRuntimeMode runtime_mode,
|
||||||
|
GError **error)
|
||||||
|
{
|
||||||
|
GrdContext *context;
|
||||||
|
GrdDaemonUser *daemon_user;
|
||||||
|
const char *application_id = NULL;
|
||||||
|
|
||||||
|
context = grd_context_new (runtime_mode, error);
|
||||||
|
if (!context)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
switch (runtime_mode)
|
||||||
|
{
|
||||||
|
case GRD_RUNTIME_MODE_SCREEN_SHARE:
|
||||||
|
application_id = GRD_DAEMON_USER_APPLICATION_ID;
|
||||||
|
break;
|
||||||
|
case GRD_RUNTIME_MODE_HEADLESS:
|
||||||
|
application_id = GRD_DAEMON_HEADLESS_APPLICATION_ID;
|
||||||
|
break;
|
||||||
|
case GRD_RUNTIME_MODE_SYSTEM:
|
||||||
|
case GRD_RUNTIME_MODE_HANDOVER:
|
||||||
|
g_assert_not_reached ();
|
||||||
|
}
|
||||||
|
|
||||||
|
daemon_user = g_object_new (GRD_TYPE_DAEMON_USER,
|
||||||
|
"application-id", application_id,
|
||||||
|
"flags", G_APPLICATION_IS_SERVICE,
|
||||||
|
"context", context,
|
||||||
|
NULL);
|
||||||
|
|
||||||
|
return daemon_user;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
grd_daemon_user_init (GrdDaemonUser *daemon_user)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
grd_daemon_user_startup (GApplication *app)
|
||||||
|
{
|
||||||
|
GrdDaemonUser *daemon_user = GRD_DAEMON_USER (app);
|
||||||
|
#ifdef HAVE_RDP
|
||||||
|
GCancellable *cancellable =
|
||||||
|
grd_daemon_get_cancellable (GRD_DAEMON (daemon_user));
|
||||||
|
#endif /* HAVE_RDP */
|
||||||
|
|
||||||
|
grd_daemon_acquire_mutter_dbus_proxies (GRD_DAEMON (daemon_user));
|
||||||
|
|
||||||
|
g_signal_connect (daemon_user, "mutter-proxy-acquired",
|
||||||
|
G_CALLBACK (grd_daemon_maybe_enable_services), NULL);
|
||||||
|
|
||||||
|
#ifdef HAVE_RDP
|
||||||
|
grd_dbus_remote_desktop_rdp_server_proxy_new_for_bus (
|
||||||
|
G_BUS_TYPE_SYSTEM,
|
||||||
|
G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START,
|
||||||
|
REMOTE_DESKTOP_BUS_NAME,
|
||||||
|
GRD_RDP_SERVER_OBJECT_PATH,
|
||||||
|
cancellable,
|
||||||
|
on_remote_desktop_rdp_server_proxy_acquired,
|
||||||
|
daemon_user);
|
||||||
|
#endif /* HAVE_RDP */
|
||||||
|
|
||||||
|
G_APPLICATION_CLASS (grd_daemon_user_parent_class)->startup (app);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
grd_daemon_user_shutdown (GApplication *app)
|
||||||
|
{
|
||||||
|
#ifdef HAVE_RDP
|
||||||
|
GrdDaemonUser *daemon_user = GRD_DAEMON_USER (app);
|
||||||
|
|
||||||
|
g_clear_object (&daemon_user->system_rdp_server);
|
||||||
|
if (daemon_user->prompt_cancellable)
|
||||||
|
{
|
||||||
|
g_cancellable_cancel (daemon_user->prompt_cancellable);
|
||||||
|
g_clear_object (&daemon_user->prompt_cancellable);
|
||||||
|
}
|
||||||
|
g_clear_object (&daemon_user->prompt);
|
||||||
|
#endif /* HAVE_RDP */
|
||||||
|
|
||||||
|
G_APPLICATION_CLASS (grd_daemon_user_parent_class)->shutdown (app);
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
grd_daemon_user_is_ready (GrdDaemon *daemon)
|
||||||
|
{
|
||||||
|
GrdContext *context = grd_daemon_get_context (daemon);
|
||||||
|
|
||||||
|
if (!grd_context_get_mutter_remote_desktop_proxy (context) ||
|
||||||
|
!grd_context_get_mutter_screen_cast_proxy (context))
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
grd_daemon_user_class_init (GrdDaemonUserClass *klass)
|
||||||
|
{
|
||||||
|
GApplicationClass *g_application_class = G_APPLICATION_CLASS (klass);
|
||||||
|
GrdDaemonClass *daemon_class = GRD_DAEMON_CLASS (klass);
|
||||||
|
|
||||||
|
g_application_class->startup = grd_daemon_user_startup;
|
||||||
|
g_application_class->shutdown = grd_daemon_user_shutdown;
|
||||||
|
|
||||||
|
daemon_class->is_daemon_ready = grd_daemon_user_is_ready;
|
||||||
|
}
|
||||||
34
grd-daemon-user.h
Normal file
34
grd-daemon-user.h
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2022 Red Hat Inc.
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License as
|
||||||
|
* published by the Free Software Foundation; either version 2 of the
|
||||||
|
* License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
|
||||||
|
* 02111-1307, USA.
|
||||||
|
*
|
||||||
|
* Written by:
|
||||||
|
* Jonas Ådahl <jadahl@gmail.com>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "grd-daemon.h"
|
||||||
|
|
||||||
|
#define GRD_TYPE_DAEMON_USER (grd_daemon_user_get_type ())
|
||||||
|
G_DECLARE_FINAL_TYPE (GrdDaemonUser,
|
||||||
|
grd_daemon_user,
|
||||||
|
GRD, DAEMON_USER,
|
||||||
|
GrdDaemon)
|
||||||
|
|
||||||
|
GrdDaemonUser *grd_daemon_user_new (GrdRuntimeMode runtime_mode,
|
||||||
|
GError **error);
|
||||||
232
grd-daemon-utils.c
Normal file
232
grd-daemon-utils.c
Normal file
@@ -0,0 +1,232 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2023 SUSE Software Solutions Germany GmbH
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License as
|
||||||
|
* published by the Free Software Foundation; either version 2 of the
|
||||||
|
* License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
|
||||||
|
* 02111-1307, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
|
#include "grd-daemon-utils.h"
|
||||||
|
|
||||||
|
#include <systemd/sd-login.h>
|
||||||
|
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
GSM_LOGOUT_MODE_NORMAL = 0,
|
||||||
|
GSM_LOGOUT_MODE_NO_CONFIRMATION,
|
||||||
|
GSM_LOGOUT_MODE_FORCE,
|
||||||
|
};
|
||||||
|
|
||||||
|
gboolean
|
||||||
|
grd_get_pid_of_sender_sync (GDBusConnection *connection,
|
||||||
|
const char *name,
|
||||||
|
pid_t *out_pid,
|
||||||
|
GCancellable *cancellable,
|
||||||
|
GError **error)
|
||||||
|
{
|
||||||
|
g_autoptr (GVariant) result = NULL;
|
||||||
|
uint32_t pid;
|
||||||
|
|
||||||
|
g_return_val_if_fail (G_IS_DBUS_CONNECTION (connection), FALSE);
|
||||||
|
g_return_val_if_fail (name != NULL, FALSE);
|
||||||
|
|
||||||
|
g_assert (out_pid);
|
||||||
|
|
||||||
|
result = g_dbus_connection_call_sync (connection,
|
||||||
|
"org.freedesktop.DBus",
|
||||||
|
"/org/freedesktop/DBus",
|
||||||
|
"org.freedesktop.DBus",
|
||||||
|
"GetConnectionUnixProcessID",
|
||||||
|
g_variant_new ("(s)", name),
|
||||||
|
G_VARIANT_TYPE ("(u)"),
|
||||||
|
G_DBUS_CALL_FLAGS_NONE,
|
||||||
|
-1,
|
||||||
|
cancellable, error);
|
||||||
|
if (!result)
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
g_variant_get (result, "(u)", &pid);
|
||||||
|
|
||||||
|
*out_pid = (pid_t) pid;
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
gboolean
|
||||||
|
grd_get_uid_of_sender_sync (GDBusConnection *connection,
|
||||||
|
const char *name,
|
||||||
|
uid_t *out_uid,
|
||||||
|
GCancellable *cancellable,
|
||||||
|
GError **error)
|
||||||
|
{
|
||||||
|
g_autoptr (GVariant) result = NULL;
|
||||||
|
uint32_t uid;
|
||||||
|
|
||||||
|
g_return_val_if_fail (G_IS_DBUS_CONNECTION (connection), FALSE);
|
||||||
|
g_return_val_if_fail (name != NULL, FALSE);
|
||||||
|
|
||||||
|
g_assert (out_uid);
|
||||||
|
|
||||||
|
result = g_dbus_connection_call_sync (connection,
|
||||||
|
"org.freedesktop.DBus",
|
||||||
|
"/org/freedesktop/DBus",
|
||||||
|
"org.freedesktop.DBus",
|
||||||
|
"GetConnectionUnixUser",
|
||||||
|
g_variant_new ("(s)", name),
|
||||||
|
G_VARIANT_TYPE ("(u)"),
|
||||||
|
G_DBUS_CALL_FLAGS_NONE,
|
||||||
|
-1,
|
||||||
|
cancellable, error);
|
||||||
|
if (!result)
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
g_variant_get (result, "(u)", &uid);
|
||||||
|
|
||||||
|
*out_uid = (uid_t) uid;
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *
|
||||||
|
grd_get_session_id_from_pid (pid_t pid)
|
||||||
|
{
|
||||||
|
char *session_id = NULL;
|
||||||
|
int res;
|
||||||
|
|
||||||
|
res = sd_pid_get_session (pid, &session_id);
|
||||||
|
if (res < 0 && res != -ENODATA)
|
||||||
|
{
|
||||||
|
g_warning ("Failed to retrieve session information for "
|
||||||
|
"pid %d: %s", (int) pid, strerror (-res));
|
||||||
|
}
|
||||||
|
|
||||||
|
return g_steal_pointer (&session_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
grd_sd_session_is_graphical (const char *session_id)
|
||||||
|
{
|
||||||
|
const char * const graphical_session_types[] = { "wayland", "x11", NULL };
|
||||||
|
int res;
|
||||||
|
g_autofree char *type = NULL;
|
||||||
|
|
||||||
|
res = sd_session_get_type (session_id, &type);
|
||||||
|
if (res < 0)
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
return g_strv_contains (graphical_session_types, type);
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
grd_sd_session_is_active (const char *session_id)
|
||||||
|
{
|
||||||
|
const char * const active_states[] = { "active", "online", NULL };
|
||||||
|
int res;
|
||||||
|
g_autofree char *state = NULL;
|
||||||
|
|
||||||
|
res = sd_session_get_state (session_id, &state);
|
||||||
|
if (res < 0)
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
return g_strv_contains (active_states, state);
|
||||||
|
}
|
||||||
|
|
||||||
|
char *
|
||||||
|
grd_get_session_id_from_uid (uid_t uid)
|
||||||
|
{
|
||||||
|
g_auto (GStrv) sessions = NULL;
|
||||||
|
char *session_id = NULL;
|
||||||
|
int n_sessions;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
n_sessions = sd_uid_get_sessions (uid, 0, &sessions);
|
||||||
|
|
||||||
|
for (i = n_sessions - 1; i >= 0; i--)
|
||||||
|
{
|
||||||
|
if (!grd_sd_session_is_graphical (sessions[i]))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (!grd_sd_session_is_active (sessions[i]))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
session_id = sessions[i];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return g_strdup (session_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
gboolean
|
||||||
|
grd_is_remote_login (void)
|
||||||
|
{
|
||||||
|
g_autofree char *session_id = NULL;
|
||||||
|
g_autofree char *class = NULL;
|
||||||
|
pid_t pid;
|
||||||
|
uid_t uid;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
pid = getpid ();
|
||||||
|
session_id = grd_get_session_id_from_pid (pid);
|
||||||
|
if (!session_id)
|
||||||
|
{
|
||||||
|
uid = getuid ();
|
||||||
|
session_id = grd_get_session_id_from_uid (uid);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!session_id)
|
||||||
|
{
|
||||||
|
g_warning ("Couldn't get session id");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = sd_session_get_class (session_id, &class);
|
||||||
|
if (ret < 0)
|
||||||
|
{
|
||||||
|
g_warning ("Couldn't determine session class: %s", g_strerror (-ret));
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return g_strcmp0 (class, "greeter") == 0 &&
|
||||||
|
sd_session_is_remote (session_id) == 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
grd_session_manager_call_logout_sync (void)
|
||||||
|
{
|
||||||
|
g_autoptr (GDBusConnection) connection = NULL;
|
||||||
|
g_autoptr (GError) error = NULL;
|
||||||
|
|
||||||
|
connection = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, &error);
|
||||||
|
if (!connection)
|
||||||
|
{
|
||||||
|
g_warning ("Couldn't get session bus to logout: %s", error->message);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
g_dbus_connection_call_sync (connection,
|
||||||
|
"org.gnome.SessionManager",
|
||||||
|
"/org/gnome/SessionManager",
|
||||||
|
"org.gnome.SessionManager",
|
||||||
|
"Logout",
|
||||||
|
g_variant_new ("(u)",
|
||||||
|
GSM_LOGOUT_MODE_NO_CONFIRMATION),
|
||||||
|
NULL,
|
||||||
|
G_DBUS_CALL_FLAGS_NONE,
|
||||||
|
-1,
|
||||||
|
NULL, &error);
|
||||||
|
if (error)
|
||||||
|
g_warning ("Couldn't logout of session: %s", error->message);
|
||||||
|
}
|
||||||
42
grd-daemon-utils.h
Normal file
42
grd-daemon-utils.h
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2023 SUSE Software Solutions Germany GmbH
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License as
|
||||||
|
* published by the Free Software Foundation; either version 2 of the
|
||||||
|
* License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
|
||||||
|
* 02111-1307, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <gio/gio.h>
|
||||||
|
|
||||||
|
gboolean grd_get_pid_of_sender_sync (GDBusConnection *connection,
|
||||||
|
const char *name,
|
||||||
|
pid_t *out_pid,
|
||||||
|
GCancellable *cancellable,
|
||||||
|
GError **error);
|
||||||
|
|
||||||
|
gboolean grd_get_uid_of_sender_sync (GDBusConnection *connection,
|
||||||
|
const char *name,
|
||||||
|
uid_t *out_uid,
|
||||||
|
GCancellable *cancellable,
|
||||||
|
GError **error);
|
||||||
|
|
||||||
|
char *grd_get_session_id_from_pid (pid_t pid);
|
||||||
|
|
||||||
|
char *grd_get_session_id_from_uid (uid_t uid);
|
||||||
|
|
||||||
|
gboolean grd_is_remote_login (void);
|
||||||
|
|
||||||
|
void grd_session_manager_call_logout_sync (void);
|
||||||
1248
grd-daemon.c
Normal file
1248
grd-daemon.c
Normal file
File diff suppressed because it is too large
Load Diff
53
grd-daemon.h
Normal file
53
grd-daemon.h
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2015 Red Hat Inc.
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License as
|
||||||
|
* published by the Free Software Foundation; either version 2 of the
|
||||||
|
* License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
|
||||||
|
* 02111-1307, USA.
|
||||||
|
*
|
||||||
|
* Written by:
|
||||||
|
* Jonas Ådahl <jadahl@gmail.com>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <gio/gio.h>
|
||||||
|
|
||||||
|
#include "grd-context.h"
|
||||||
|
|
||||||
|
typedef struct _GrdDaemon GrdDaemon;
|
||||||
|
|
||||||
|
#define GRD_TYPE_DAEMON (grd_daemon_get_type ())
|
||||||
|
G_DECLARE_DERIVABLE_TYPE (GrdDaemon, grd_daemon, GRD, DAEMON, GApplication)
|
||||||
|
|
||||||
|
struct _GrdDaemonClass
|
||||||
|
{
|
||||||
|
GApplicationClass parent_class;
|
||||||
|
|
||||||
|
gboolean (*is_daemon_ready) (GrdDaemon *daemon);
|
||||||
|
};
|
||||||
|
|
||||||
|
GCancellable *grd_daemon_get_cancellable (GrdDaemon *daemon);
|
||||||
|
|
||||||
|
GrdContext *grd_daemon_get_context (GrdDaemon *daemon);
|
||||||
|
|
||||||
|
GrdRdpServer *grd_daemon_get_rdp_server (GrdDaemon *daemon);
|
||||||
|
|
||||||
|
void grd_daemon_restart_rdp_server_with_delay (GrdDaemon *daemon);
|
||||||
|
|
||||||
|
void grd_daemon_maybe_enable_services (GrdDaemon *daemon);
|
||||||
|
|
||||||
|
void grd_daemon_disable_services (GrdDaemon *daemon);
|
||||||
|
|
||||||
|
void grd_daemon_acquire_mutter_dbus_proxies (GrdDaemon *daemon);
|
||||||
188
grd-damage-detector-sw.c
Normal file
188
grd-damage-detector-sw.c
Normal file
@@ -0,0 +1,188 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2025 Pascal Nowack
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License as
|
||||||
|
* published by the Free Software Foundation; either version 2 of the
|
||||||
|
* License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
|
||||||
|
* 02111-1307, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
|
#include "grd-damage-detector-sw.h"
|
||||||
|
|
||||||
|
#include "grd-damage-utils.h"
|
||||||
|
#include "grd-local-buffer.h"
|
||||||
|
#include "grd-utils.h"
|
||||||
|
|
||||||
|
#define TILE_WIDTH 64
|
||||||
|
#define TILE_HEIGHT 64
|
||||||
|
|
||||||
|
struct _GrdDamageDetectorSw
|
||||||
|
{
|
||||||
|
GObject parent;
|
||||||
|
|
||||||
|
uint32_t surface_width;
|
||||||
|
uint32_t surface_height;
|
||||||
|
|
||||||
|
uint32_t damage_buffer_width;
|
||||||
|
uint32_t damage_buffer_height;
|
||||||
|
|
||||||
|
uint32_t *damage_buffer;
|
||||||
|
uint32_t damage_buffer_length;
|
||||||
|
};
|
||||||
|
|
||||||
|
G_DEFINE_TYPE (GrdDamageDetectorSw, grd_damage_detector_sw,
|
||||||
|
G_TYPE_OBJECT)
|
||||||
|
|
||||||
|
uint32_t *
|
||||||
|
grd_damage_detector_sw_get_damage_buffer (GrdDamageDetectorSw *damage_detector)
|
||||||
|
{
|
||||||
|
return damage_detector->damage_buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t
|
||||||
|
grd_damage_detector_sw_get_damage_buffer_length (GrdDamageDetectorSw *damage_detector)
|
||||||
|
{
|
||||||
|
return damage_detector->damage_buffer_length;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
invalidate_surface (GrdDamageDetectorSw *damage_detector)
|
||||||
|
{
|
||||||
|
uint32_t i;
|
||||||
|
|
||||||
|
for (i = 0; i < damage_detector->damage_buffer_length; ++i)
|
||||||
|
damage_detector->damage_buffer[i] = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
compute_frame_damage (GrdDamageDetectorSw *damage_detector,
|
||||||
|
GrdLocalBuffer *local_buffer_new,
|
||||||
|
GrdLocalBuffer *local_buffer_old)
|
||||||
|
{
|
||||||
|
uint32_t surface_width = damage_detector->surface_width;
|
||||||
|
uint32_t surface_height = damage_detector->surface_height;
|
||||||
|
uint32_t local_buffer_stride;
|
||||||
|
uint32_t damage_buffer_stride;
|
||||||
|
uint32_t x, y;
|
||||||
|
|
||||||
|
g_assert (local_buffer_new);
|
||||||
|
g_assert (local_buffer_old);
|
||||||
|
|
||||||
|
g_assert (grd_local_buffer_get_buffer_stride (local_buffer_new) ==
|
||||||
|
grd_local_buffer_get_buffer_stride (local_buffer_old));
|
||||||
|
local_buffer_stride = grd_local_buffer_get_buffer_stride (local_buffer_new);
|
||||||
|
|
||||||
|
damage_buffer_stride = damage_detector->damage_buffer_width;
|
||||||
|
|
||||||
|
for (y = 0; y < damage_detector->damage_buffer_height; ++y)
|
||||||
|
{
|
||||||
|
for (x = 0; x < damage_detector->damage_buffer_width; ++x)
|
||||||
|
{
|
||||||
|
cairo_rectangle_int_t tile;
|
||||||
|
uint32_t tile_damaged = 0;
|
||||||
|
uint32_t buffer_pos;
|
||||||
|
|
||||||
|
tile.x = x * TILE_WIDTH;
|
||||||
|
tile.y = y * TILE_HEIGHT;
|
||||||
|
tile.width = surface_width - tile.x < TILE_WIDTH ?
|
||||||
|
surface_width - tile.x : TILE_WIDTH;
|
||||||
|
tile.height = surface_height - tile.y < TILE_HEIGHT ?
|
||||||
|
surface_height - tile.y : TILE_HEIGHT;
|
||||||
|
|
||||||
|
if (grd_is_tile_dirty (&tile,
|
||||||
|
grd_local_buffer_get_buffer (local_buffer_new),
|
||||||
|
grd_local_buffer_get_buffer (local_buffer_old),
|
||||||
|
local_buffer_stride, 4))
|
||||||
|
tile_damaged = 1;
|
||||||
|
|
||||||
|
buffer_pos = y * damage_buffer_stride + x;
|
||||||
|
damage_detector->damage_buffer[buffer_pos] = tile_damaged;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
grd_damage_detector_sw_compute_damage (GrdDamageDetectorSw *damage_detector,
|
||||||
|
GrdLocalBuffer *local_buffer_new,
|
||||||
|
GrdLocalBuffer *local_buffer_old)
|
||||||
|
{
|
||||||
|
if (!local_buffer_old)
|
||||||
|
{
|
||||||
|
invalidate_surface (damage_detector);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
compute_frame_damage (damage_detector, local_buffer_new, local_buffer_old);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
create_damage_buffer (GrdDamageDetectorSw *damage_detector)
|
||||||
|
{
|
||||||
|
uint32_t surface_width = damage_detector->surface_width;
|
||||||
|
uint32_t surface_height = damage_detector->surface_height;
|
||||||
|
uint32_t damage_buffer_width;
|
||||||
|
uint32_t damage_buffer_height;
|
||||||
|
uint32_t damage_buffer_length;
|
||||||
|
|
||||||
|
damage_buffer_width = grd_get_aligned_size (surface_width, TILE_WIDTH) /
|
||||||
|
TILE_WIDTH;
|
||||||
|
damage_buffer_height = grd_get_aligned_size (surface_height, TILE_HEIGHT) /
|
||||||
|
TILE_HEIGHT;
|
||||||
|
damage_buffer_length = damage_buffer_width * damage_buffer_height;
|
||||||
|
|
||||||
|
damage_detector->damage_buffer_width = damage_buffer_width;
|
||||||
|
damage_detector->damage_buffer_height = damage_buffer_height;
|
||||||
|
|
||||||
|
damage_detector->damage_buffer = g_new0 (uint32_t, damage_buffer_length);
|
||||||
|
damage_detector->damage_buffer_length = damage_buffer_length;
|
||||||
|
}
|
||||||
|
|
||||||
|
GrdDamageDetectorSw *
|
||||||
|
grd_damage_detector_sw_new (uint32_t surface_width,
|
||||||
|
uint32_t surface_height)
|
||||||
|
{
|
||||||
|
GrdDamageDetectorSw *damage_detector;
|
||||||
|
|
||||||
|
damage_detector = g_object_new (GRD_TYPE_DAMAGE_DETECTOR_SW, NULL);
|
||||||
|
damage_detector->surface_width = surface_width;
|
||||||
|
damage_detector->surface_height = surface_height;
|
||||||
|
|
||||||
|
create_damage_buffer (damage_detector);
|
||||||
|
|
||||||
|
return damage_detector;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
grd_damage_detector_sw_dispose (GObject *object)
|
||||||
|
{
|
||||||
|
GrdDamageDetectorSw *damage_detector = GRD_DAMAGE_DETECTOR_SW (object);
|
||||||
|
|
||||||
|
g_clear_pointer (&damage_detector->damage_buffer, g_free);
|
||||||
|
|
||||||
|
G_OBJECT_CLASS (grd_damage_detector_sw_parent_class)->dispose (object);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
grd_damage_detector_sw_init (GrdDamageDetectorSw *damage_detector)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
grd_damage_detector_sw_class_init (GrdDamageDetectorSwClass *klass)
|
||||||
|
{
|
||||||
|
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
||||||
|
|
||||||
|
object_class->dispose = grd_damage_detector_sw_dispose;
|
||||||
|
}
|
||||||
40
grd-damage-detector-sw.h
Normal file
40
grd-damage-detector-sw.h
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2025 Pascal Nowack
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License as
|
||||||
|
* published by the Free Software Foundation; either version 2 of the
|
||||||
|
* License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
|
||||||
|
* 02111-1307, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <glib-object.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#include "grd-types.h"
|
||||||
|
|
||||||
|
#define GRD_TYPE_DAMAGE_DETECTOR_SW (grd_damage_detector_sw_get_type ())
|
||||||
|
G_DECLARE_FINAL_TYPE (GrdDamageDetectorSw, grd_damage_detector_sw,
|
||||||
|
GRD, DAMAGE_DETECTOR_SW, GObject)
|
||||||
|
|
||||||
|
GrdDamageDetectorSw *grd_damage_detector_sw_new (uint32_t surface_width,
|
||||||
|
uint32_t surface_height);
|
||||||
|
|
||||||
|
uint32_t *grd_damage_detector_sw_get_damage_buffer (GrdDamageDetectorSw *damage_detector);
|
||||||
|
|
||||||
|
uint32_t grd_damage_detector_sw_get_damage_buffer_length (GrdDamageDetectorSw *damage_detector);
|
||||||
|
|
||||||
|
void grd_damage_detector_sw_compute_damage (GrdDamageDetectorSw *damage_detector,
|
||||||
|
GrdLocalBuffer *local_buffer_new,
|
||||||
|
GrdLocalBuffer *local_buffer_old);
|
||||||
92
grd-damage-utils.c
Normal file
92
grd-damage-utils.c
Normal file
@@ -0,0 +1,92 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2020 Pascal Nowack
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License as
|
||||||
|
* published by the Free Software Foundation; either version 2 of the
|
||||||
|
* License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
|
||||||
|
* 02111-1307, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
|
#include "grd-damage-utils.h"
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
bool
|
||||||
|
grd_is_tile_dirty (cairo_rectangle_int_t *tile,
|
||||||
|
uint8_t *current_data,
|
||||||
|
uint8_t *prev_data,
|
||||||
|
uint32_t stride,
|
||||||
|
uint32_t bytes_per_pixel)
|
||||||
|
{
|
||||||
|
uint32_t y;
|
||||||
|
|
||||||
|
for (y = tile->y; y < tile->y + tile->height; ++y)
|
||||||
|
{
|
||||||
|
if (memcmp (prev_data + y * stride + tile->x * bytes_per_pixel,
|
||||||
|
current_data + y * stride + tile->x * bytes_per_pixel,
|
||||||
|
tile->width * bytes_per_pixel))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
cairo_region_t *
|
||||||
|
grd_get_damage_region (uint8_t *current_data,
|
||||||
|
uint8_t *prev_data,
|
||||||
|
uint32_t surface_width,
|
||||||
|
uint32_t surface_height,
|
||||||
|
uint32_t tile_width,
|
||||||
|
uint32_t tile_height,
|
||||||
|
uint32_t stride,
|
||||||
|
uint32_t bytes_per_pixel)
|
||||||
|
{
|
||||||
|
cairo_region_t *damage_region;
|
||||||
|
cairo_rectangle_int_t tile;
|
||||||
|
uint32_t cols, rows;
|
||||||
|
uint32_t x, y;
|
||||||
|
|
||||||
|
damage_region = cairo_region_create ();
|
||||||
|
if (current_data == NULL || prev_data == NULL)
|
||||||
|
{
|
||||||
|
tile.x = tile.y = 0;
|
||||||
|
tile.width = surface_width;
|
||||||
|
tile.height = surface_height;
|
||||||
|
cairo_region_union_rectangle (damage_region, &tile);
|
||||||
|
|
||||||
|
return damage_region;
|
||||||
|
}
|
||||||
|
|
||||||
|
cols = surface_width / tile_width + (surface_width % tile_width ? 1 : 0);
|
||||||
|
rows = surface_height / tile_height + (surface_height % tile_height ? 1 : 0);
|
||||||
|
|
||||||
|
for (y = 0; y < rows; ++y)
|
||||||
|
{
|
||||||
|
for (x = 0; x < cols; ++x)
|
||||||
|
{
|
||||||
|
tile.x = x * tile_width;
|
||||||
|
tile.y = y * tile_height;
|
||||||
|
tile.width = surface_width - tile.x < tile_width ? surface_width - tile.x
|
||||||
|
: tile_width;
|
||||||
|
tile.height = surface_height - tile.y < tile_height ? surface_height - tile.y
|
||||||
|
: tile_height;
|
||||||
|
|
||||||
|
if (grd_is_tile_dirty (&tile, current_data, prev_data, stride, bytes_per_pixel))
|
||||||
|
cairo_region_union_rectangle (damage_region, &tile);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return damage_region;
|
||||||
|
}
|
||||||
39
grd-damage-utils.h
Normal file
39
grd-damage-utils.h
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2020 Pascal Nowack
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License as
|
||||||
|
* published by the Free Software Foundation; either version 2 of the
|
||||||
|
* License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
|
||||||
|
* 02111-1307, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <cairo/cairo.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
cairo_region_t *grd_get_damage_region (uint8_t *current_data,
|
||||||
|
uint8_t *prev_data,
|
||||||
|
uint32_t surface_width,
|
||||||
|
uint32_t surface_height,
|
||||||
|
uint32_t tile_width,
|
||||||
|
uint32_t tile_height,
|
||||||
|
uint32_t stride,
|
||||||
|
uint32_t bytes_per_pixel);
|
||||||
|
|
||||||
|
bool grd_is_tile_dirty (cairo_rectangle_int_t *tile,
|
||||||
|
uint8_t *current_data,
|
||||||
|
uint8_t *prev_data,
|
||||||
|
uint32_t stride,
|
||||||
|
uint32_t bytes_per_pixel);
|
||||||
62
grd-debug.c
Normal file
62
grd-debug.c
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2015,2022 Red Hat Inc.
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License as
|
||||||
|
* published by the Free Software Foundation; either version 2 of the
|
||||||
|
* License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
|
||||||
|
* 02111-1307, USA.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
|
#include "grd-debug.h"
|
||||||
|
|
||||||
|
#include <glib.h>
|
||||||
|
|
||||||
|
static const GDebugKey grd_debug_keys[] = {
|
||||||
|
{ "vnc", GRD_DEBUG_VNC },
|
||||||
|
{ "tpm", GRD_DEBUG_TPM },
|
||||||
|
{ "vk-validation", GRD_DEBUG_VK_VALIDATION },
|
||||||
|
{ "vk-times", GRD_DEBUG_VK_TIMES },
|
||||||
|
{ "va-times", GRD_DEBUG_VA_TIMES },
|
||||||
|
{ "vkva-renderer", GRD_DEBUG_VKVA },
|
||||||
|
};
|
||||||
|
|
||||||
|
static GrdDebugFlags debug_flags;
|
||||||
|
|
||||||
|
static gpointer
|
||||||
|
init_debug_flags (gpointer user_data)
|
||||||
|
{
|
||||||
|
const char *debug_env;
|
||||||
|
|
||||||
|
debug_env = g_getenv ("GNOME_REMOTE_DESKTOP_DEBUG");
|
||||||
|
if (debug_env)
|
||||||
|
{
|
||||||
|
debug_flags = g_parse_debug_string (debug_env,
|
||||||
|
grd_debug_keys,
|
||||||
|
G_N_ELEMENTS (grd_debug_keys));
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
GrdDebugFlags
|
||||||
|
grd_get_debug_flags (void)
|
||||||
|
{
|
||||||
|
static GOnce debug_flags_once = G_ONCE_INIT;
|
||||||
|
|
||||||
|
g_once (&debug_flags_once, init_debug_flags, NULL);
|
||||||
|
|
||||||
|
return debug_flags;
|
||||||
|
}
|
||||||
34
grd-debug.h
Normal file
34
grd-debug.h
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2015,2022 Red Hat Inc.
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License as
|
||||||
|
* published by the Free Software Foundation; either version 2 of the
|
||||||
|
* License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
|
||||||
|
* 02111-1307, USA.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
typedef enum _GrdDebugFlags
|
||||||
|
{
|
||||||
|
GRD_DEBUG_NONE = 0,
|
||||||
|
GRD_DEBUG_VNC = 1 << 0,
|
||||||
|
GRD_DEBUG_TPM = 1 << 1,
|
||||||
|
GRD_DEBUG_VK_VALIDATION = 1 << 2,
|
||||||
|
GRD_DEBUG_VK_TIMES = 1 << 3,
|
||||||
|
GRD_DEBUG_VA_TIMES = 1 << 4,
|
||||||
|
GRD_DEBUG_VKVA = 1 << 5,
|
||||||
|
} GrdDebugFlags;
|
||||||
|
|
||||||
|
GrdDebugFlags grd_get_debug_flags (void);
|
||||||
422
grd-decode-session-sw-avc.c
Normal file
422
grd-decode-session-sw-avc.c
Normal file
@@ -0,0 +1,422 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2025 Pascal Nowack
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License as
|
||||||
|
* published by the Free Software Foundation; either version 2 of the
|
||||||
|
* License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
|
||||||
|
* 02111-1307, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
|
#include "grd-decode-session-sw-avc.h"
|
||||||
|
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <freerdp/codec/color.h>
|
||||||
|
#include <freerdp/codec/h264.h>
|
||||||
|
#include <glib/gstdio.h>
|
||||||
|
#include <sys/mman.h>
|
||||||
|
|
||||||
|
#include "grd-sample-buffer.h"
|
||||||
|
#include "grd-utils.h"
|
||||||
|
|
||||||
|
#define MAX_BASE_SIZE (1 << 18)
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
uint8_t *data;
|
||||||
|
|
||||||
|
GrdSampleBuffer *sample_buffer;
|
||||||
|
} BitstreamBuffer;
|
||||||
|
|
||||||
|
struct _GrdDecodeSessionSwAVC
|
||||||
|
{
|
||||||
|
GrdDecodeSession parent;
|
||||||
|
|
||||||
|
H264_CONTEXT *h264_context;
|
||||||
|
uint32_t surface_width;
|
||||||
|
uint32_t surface_height;
|
||||||
|
|
||||||
|
GHashTable *bitstream_buffers;
|
||||||
|
GHashTable *pipewire_buffers;
|
||||||
|
|
||||||
|
GMutex pending_frames_mutex;
|
||||||
|
GHashTable *pending_frames;
|
||||||
|
};
|
||||||
|
|
||||||
|
G_DEFINE_TYPE (GrdDecodeSessionSwAVC, grd_decode_session_sw_avc,
|
||||||
|
GRD_TYPE_DECODE_SESSION)
|
||||||
|
|
||||||
|
static BitstreamBuffer *
|
||||||
|
bitstream_buffer_new (size_t size)
|
||||||
|
{
|
||||||
|
BitstreamBuffer *bitstream_buffer;
|
||||||
|
|
||||||
|
bitstream_buffer = g_new0 (BitstreamBuffer, 1);
|
||||||
|
|
||||||
|
bitstream_buffer->data = g_malloc0 (size);
|
||||||
|
bitstream_buffer->sample_buffer =
|
||||||
|
grd_sample_buffer_new (bitstream_buffer->data, size);
|
||||||
|
|
||||||
|
return bitstream_buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
bitstream_buffer_free (BitstreamBuffer *bitstream_buffer)
|
||||||
|
{
|
||||||
|
g_clear_pointer (&bitstream_buffer->sample_buffer, grd_sample_buffer_free);
|
||||||
|
g_clear_pointer (&bitstream_buffer->data, g_free);
|
||||||
|
|
||||||
|
g_free (bitstream_buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
grd_decode_session_sw_avc_get_drm_format_modifiers (GrdDecodeSession *decode_session,
|
||||||
|
uint32_t drm_format,
|
||||||
|
uint32_t *out_n_modifiers,
|
||||||
|
uint64_t **out_modifiers)
|
||||||
|
{
|
||||||
|
*out_n_modifiers = 0;
|
||||||
|
*out_modifiers = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
grd_decode_session_sw_avc_reset (GrdDecodeSession *decode_session,
|
||||||
|
uint32_t surface_width,
|
||||||
|
uint32_t surface_height,
|
||||||
|
uint64_t drm_format_modifier,
|
||||||
|
GError **error)
|
||||||
|
{
|
||||||
|
GrdDecodeSessionSwAVC *decode_session_sw_avc =
|
||||||
|
GRD_DECODE_SESSION_SW_AVC (decode_session);
|
||||||
|
|
||||||
|
decode_session_sw_avc->surface_width = surface_width;
|
||||||
|
decode_session_sw_avc->surface_height = surface_height;
|
||||||
|
|
||||||
|
if (!h264_context_reset (decode_session_sw_avc->h264_context, surface_width, surface_height))
|
||||||
|
{
|
||||||
|
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
|
||||||
|
"Failed to reset H.264 decode context");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
allocate_mem_fd_buffer (GrdDecodeSessionSwAVC *decode_session_sw_avc,
|
||||||
|
struct pw_buffer *pw_buffer,
|
||||||
|
GError **error)
|
||||||
|
{
|
||||||
|
uint32_t surface_width = decode_session_sw_avc->surface_width;
|
||||||
|
uint32_t surface_height = decode_session_sw_avc->surface_height;
|
||||||
|
struct spa_data *spa_data = &pw_buffer->buffer->datas[0];
|
||||||
|
g_autofd int fd = -1;
|
||||||
|
unsigned int seals;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
fd = memfd_create ("grd-decode-session-mem-fd",
|
||||||
|
MFD_CLOEXEC | MFD_ALLOW_SEALING);
|
||||||
|
if (fd == -1)
|
||||||
|
{
|
||||||
|
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
|
||||||
|
"Failed to create mem-fd: %s", strerror (errno));
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
spa_data->chunk->stride = surface_width * 4;
|
||||||
|
spa_data->maxsize = spa_data->chunk->stride * surface_height;
|
||||||
|
spa_data->mapoffset = 0;
|
||||||
|
|
||||||
|
do
|
||||||
|
ret = ftruncate (fd, spa_data->maxsize);
|
||||||
|
while (ret == -1 && errno == EINTR);
|
||||||
|
if (ret < 0)
|
||||||
|
{
|
||||||
|
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
|
||||||
|
"Failed to truncate mem-fd: %s", strerror (errno));
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
seals = F_SEAL_GROW | F_SEAL_SHRINK | F_SEAL_SEAL;
|
||||||
|
if (fcntl (fd, F_ADD_SEALS, seals) < 0)
|
||||||
|
{
|
||||||
|
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
|
||||||
|
"Failed to add seals to mem-fd: %s", strerror (errno));
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
spa_data->data = mmap (NULL, spa_data->maxsize,
|
||||||
|
PROT_READ | PROT_WRITE,
|
||||||
|
MAP_SHARED,
|
||||||
|
fd,
|
||||||
|
spa_data->mapoffset);
|
||||||
|
if (spa_data->data == MAP_FAILED)
|
||||||
|
{
|
||||||
|
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
|
||||||
|
"Failed to mmap memory: %s", strerror (errno));
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
spa_data->type = SPA_DATA_MemFd;
|
||||||
|
spa_data->flags = SPA_DATA_FLAG_READABLE | SPA_DATA_FLAG_MAPPABLE;
|
||||||
|
spa_data->fd = g_steal_fd (&fd);
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
grd_decode_session_sw_avc_register_buffer (GrdDecodeSession *decode_session,
|
||||||
|
struct pw_buffer *pw_buffer,
|
||||||
|
GError **error)
|
||||||
|
{
|
||||||
|
GrdDecodeSessionSwAVC *decode_session_sw_avc =
|
||||||
|
GRD_DECODE_SESSION_SW_AVC (decode_session);
|
||||||
|
uint32_t surface_width = decode_session_sw_avc->surface_width;
|
||||||
|
uint32_t surface_height = decode_session_sw_avc->surface_height;
|
||||||
|
BitstreamBuffer *bitstream_buffer;
|
||||||
|
size_t width_in_mbs;
|
||||||
|
size_t height_in_mbs;
|
||||||
|
size_t size;
|
||||||
|
|
||||||
|
if (!allocate_mem_fd_buffer (decode_session_sw_avc, pw_buffer, error))
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
width_in_mbs = grd_get_aligned_size (surface_width, 16) / 16;
|
||||||
|
height_in_mbs = grd_get_aligned_size (surface_height, 16) / 16;
|
||||||
|
size = MAX_BASE_SIZE + width_in_mbs * height_in_mbs * 400;
|
||||||
|
|
||||||
|
bitstream_buffer = bitstream_buffer_new (size);
|
||||||
|
|
||||||
|
g_hash_table_insert (decode_session_sw_avc->bitstream_buffers,
|
||||||
|
pw_buffer, bitstream_buffer);
|
||||||
|
g_hash_table_insert (decode_session_sw_avc->pipewire_buffers,
|
||||||
|
bitstream_buffer->sample_buffer, pw_buffer);
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
destroy_mem_fd_buffer (GrdDecodeSessionSwAVC *decode_session_sw_avc,
|
||||||
|
struct pw_buffer *pw_buffer)
|
||||||
|
{
|
||||||
|
struct spa_data *spa_data = &pw_buffer->buffer->datas[0];
|
||||||
|
|
||||||
|
if (spa_data->type != SPA_DATA_MemFd)
|
||||||
|
return;
|
||||||
|
|
||||||
|
g_assert (spa_data->data != MAP_FAILED);
|
||||||
|
g_assert (spa_data->fd > 0);
|
||||||
|
|
||||||
|
munmap (spa_data->data, spa_data->maxsize);
|
||||||
|
close (spa_data->fd);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
grd_decode_session_sw_avc_unregister_buffer (GrdDecodeSession *decode_session,
|
||||||
|
struct pw_buffer *pw_buffer)
|
||||||
|
{
|
||||||
|
GrdDecodeSessionSwAVC *decode_session_sw_avc =
|
||||||
|
GRD_DECODE_SESSION_SW_AVC (decode_session);
|
||||||
|
BitstreamBuffer *bitstream_buffer = NULL;
|
||||||
|
GrdSampleBuffer *sample_buffer;
|
||||||
|
|
||||||
|
if (!g_hash_table_lookup_extended (decode_session_sw_avc->bitstream_buffers,
|
||||||
|
pw_buffer,
|
||||||
|
NULL, (gpointer *) &bitstream_buffer))
|
||||||
|
return;
|
||||||
|
|
||||||
|
sample_buffer = bitstream_buffer->sample_buffer;
|
||||||
|
|
||||||
|
g_hash_table_remove (decode_session_sw_avc->bitstream_buffers, pw_buffer);
|
||||||
|
g_hash_table_remove (decode_session_sw_avc->pipewire_buffers, sample_buffer);
|
||||||
|
|
||||||
|
destroy_mem_fd_buffer (decode_session_sw_avc, pw_buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
static GrdSampleBuffer *
|
||||||
|
grd_decode_session_sw_avc_get_sample_buffer (GrdDecodeSession *decode_session,
|
||||||
|
struct pw_buffer *pw_buffer)
|
||||||
|
{
|
||||||
|
GrdDecodeSessionSwAVC *decode_session_sw_avc =
|
||||||
|
GRD_DECODE_SESSION_SW_AVC (decode_session);
|
||||||
|
BitstreamBuffer *bitstream_buffer = NULL;
|
||||||
|
|
||||||
|
if (!g_hash_table_lookup_extended (decode_session_sw_avc->bitstream_buffers,
|
||||||
|
pw_buffer,
|
||||||
|
NULL, (gpointer *) &bitstream_buffer))
|
||||||
|
g_assert_not_reached ();
|
||||||
|
|
||||||
|
return bitstream_buffer->sample_buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
grd_decode_session_sw_avc_submit_sample (GrdDecodeSession *decode_session,
|
||||||
|
GrdSampleBuffer *sample_buffer,
|
||||||
|
GError **error)
|
||||||
|
{
|
||||||
|
GrdDecodeSessionSwAVC *decode_session_sw_avc =
|
||||||
|
GRD_DECODE_SESSION_SW_AVC (decode_session);
|
||||||
|
|
||||||
|
g_mutex_lock (&decode_session_sw_avc->pending_frames_mutex);
|
||||||
|
g_assert (!g_hash_table_contains (decode_session_sw_avc->pending_frames,
|
||||||
|
sample_buffer));
|
||||||
|
|
||||||
|
g_hash_table_add (decode_session_sw_avc->pending_frames, sample_buffer);
|
||||||
|
g_mutex_unlock (&decode_session_sw_avc->pending_frames_mutex);
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint32_t
|
||||||
|
grd_decode_session_sw_avc_get_n_pending_frames (GrdDecodeSession *decode_session)
|
||||||
|
{
|
||||||
|
GrdDecodeSessionSwAVC *decode_session_sw_avc =
|
||||||
|
GRD_DECODE_SESSION_SW_AVC (decode_session);
|
||||||
|
g_autoptr (GMutexLocker) locker = NULL;
|
||||||
|
|
||||||
|
locker = g_mutex_locker_new (&decode_session_sw_avc->pending_frames_mutex);
|
||||||
|
return g_hash_table_size (decode_session_sw_avc->pending_frames);
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
grd_decode_session_sw_avc_decode_frame (GrdDecodeSession *decode_session,
|
||||||
|
GrdSampleBuffer *sample_buffer,
|
||||||
|
GError **error)
|
||||||
|
{
|
||||||
|
GrdDecodeSessionSwAVC *decode_session_sw_avc =
|
||||||
|
GRD_DECODE_SESSION_SW_AVC (decode_session);
|
||||||
|
uint32_t surface_width = decode_session_sw_avc->surface_width;
|
||||||
|
uint32_t surface_height = decode_session_sw_avc->surface_height;
|
||||||
|
struct pw_buffer *pw_buffer = NULL;
|
||||||
|
struct spa_data *spa_data;
|
||||||
|
RECTANGLE_16 rect = {};
|
||||||
|
int32_t ret;
|
||||||
|
|
||||||
|
g_mutex_lock (&decode_session_sw_avc->pending_frames_mutex);
|
||||||
|
if (!g_hash_table_remove (decode_session_sw_avc->pending_frames,
|
||||||
|
sample_buffer))
|
||||||
|
g_assert_not_reached ();
|
||||||
|
g_mutex_unlock (&decode_session_sw_avc->pending_frames_mutex);
|
||||||
|
|
||||||
|
if (!g_hash_table_lookup_extended (decode_session_sw_avc->pipewire_buffers,
|
||||||
|
sample_buffer,
|
||||||
|
NULL, (gpointer *) &pw_buffer))
|
||||||
|
g_assert_not_reached ();
|
||||||
|
|
||||||
|
spa_data = &pw_buffer->buffer->datas[0];
|
||||||
|
|
||||||
|
rect.left = 0;
|
||||||
|
rect.top = 0;
|
||||||
|
rect.right = surface_width;
|
||||||
|
rect.bottom = surface_height;
|
||||||
|
|
||||||
|
ret = avc420_decompress (decode_session_sw_avc->h264_context,
|
||||||
|
grd_sample_buffer_get_data_pointer (sample_buffer),
|
||||||
|
grd_sample_buffer_get_sample_size (sample_buffer),
|
||||||
|
spa_data->data, PIXEL_FORMAT_BGRA32,
|
||||||
|
spa_data->chunk->stride, surface_width, surface_height,
|
||||||
|
&rect, 1);
|
||||||
|
if (ret < 0)
|
||||||
|
{
|
||||||
|
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
|
||||||
|
"avc420_decompress failed with status code %i", -ret);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
GrdDecodeSessionSwAVC *
|
||||||
|
grd_decode_session_sw_avc_new (GError **error)
|
||||||
|
{
|
||||||
|
g_autoptr (GrdDecodeSessionSwAVC) decode_session_sw_avc = NULL;
|
||||||
|
|
||||||
|
decode_session_sw_avc = g_object_new (GRD_TYPE_DECODE_SESSION_SW_AVC, NULL);
|
||||||
|
|
||||||
|
decode_session_sw_avc->h264_context = h264_context_new (FALSE);
|
||||||
|
if (!decode_session_sw_avc->h264_context)
|
||||||
|
{
|
||||||
|
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
|
||||||
|
"Failed to create H.264 decode context");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return g_steal_pointer (&decode_session_sw_avc);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
grd_decode_session_sw_avc_dispose (GObject *object)
|
||||||
|
{
|
||||||
|
GrdDecodeSessionSwAVC *decode_session_sw_avc =
|
||||||
|
GRD_DECODE_SESSION_SW_AVC (object);
|
||||||
|
|
||||||
|
g_clear_pointer (&decode_session_sw_avc->h264_context, h264_context_free);
|
||||||
|
|
||||||
|
g_clear_pointer (&decode_session_sw_avc->pending_frames, g_hash_table_unref);
|
||||||
|
g_clear_pointer (&decode_session_sw_avc->pipewire_buffers, g_hash_table_unref);
|
||||||
|
g_clear_pointer (&decode_session_sw_avc->bitstream_buffers, g_hash_table_unref);
|
||||||
|
|
||||||
|
G_OBJECT_CLASS (grd_decode_session_sw_avc_parent_class)->dispose (object);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
grd_decode_session_sw_avc_finalize (GObject *object)
|
||||||
|
{
|
||||||
|
GrdDecodeSessionSwAVC *decode_session_sw_avc =
|
||||||
|
GRD_DECODE_SESSION_SW_AVC (object);
|
||||||
|
|
||||||
|
g_mutex_clear (&decode_session_sw_avc->pending_frames_mutex);
|
||||||
|
|
||||||
|
G_OBJECT_CLASS (grd_decode_session_sw_avc_parent_class)->finalize (object);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
grd_decode_session_sw_avc_init (GrdDecodeSessionSwAVC *decode_session_sw_avc)
|
||||||
|
{
|
||||||
|
decode_session_sw_avc->bitstream_buffers =
|
||||||
|
g_hash_table_new_full (NULL, NULL,
|
||||||
|
NULL, (GDestroyNotify) bitstream_buffer_free);
|
||||||
|
decode_session_sw_avc->pipewire_buffers = g_hash_table_new (NULL, NULL);
|
||||||
|
decode_session_sw_avc->pending_frames = g_hash_table_new (NULL, NULL);
|
||||||
|
|
||||||
|
g_mutex_init (&decode_session_sw_avc->pending_frames_mutex);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
grd_decode_session_sw_avc_class_init (GrdDecodeSessionSwAVCClass *klass)
|
||||||
|
{
|
||||||
|
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
||||||
|
GrdDecodeSessionClass *decode_session_class =
|
||||||
|
GRD_DECODE_SESSION_CLASS (klass);
|
||||||
|
|
||||||
|
object_class->dispose = grd_decode_session_sw_avc_dispose;
|
||||||
|
object_class->finalize = grd_decode_session_sw_avc_finalize;
|
||||||
|
|
||||||
|
decode_session_class->get_drm_format_modifiers =
|
||||||
|
grd_decode_session_sw_avc_get_drm_format_modifiers;
|
||||||
|
decode_session_class->reset =
|
||||||
|
grd_decode_session_sw_avc_reset;
|
||||||
|
decode_session_class->register_buffer =
|
||||||
|
grd_decode_session_sw_avc_register_buffer;
|
||||||
|
decode_session_class->unregister_buffer =
|
||||||
|
grd_decode_session_sw_avc_unregister_buffer;
|
||||||
|
decode_session_class->get_sample_buffer =
|
||||||
|
grd_decode_session_sw_avc_get_sample_buffer;
|
||||||
|
decode_session_class->submit_sample =
|
||||||
|
grd_decode_session_sw_avc_submit_sample;
|
||||||
|
decode_session_class->get_n_pending_frames =
|
||||||
|
grd_decode_session_sw_avc_get_n_pending_frames;
|
||||||
|
decode_session_class->decode_frame =
|
||||||
|
grd_decode_session_sw_avc_decode_frame;
|
||||||
|
}
|
||||||
28
grd-decode-session-sw-avc.h
Normal file
28
grd-decode-session-sw-avc.h
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2025 Pascal Nowack
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License as
|
||||||
|
* published by the Free Software Foundation; either version 2 of the
|
||||||
|
* License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
|
||||||
|
* 02111-1307, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "grd-decode-session.h"
|
||||||
|
|
||||||
|
#define GRD_TYPE_DECODE_SESSION_SW_AVC (grd_decode_session_sw_avc_get_type ())
|
||||||
|
G_DECLARE_FINAL_TYPE (GrdDecodeSessionSwAVC, grd_decode_session_sw_avc,
|
||||||
|
GRD, DECODE_SESSION_SW_AVC, GrdDecodeSession)
|
||||||
|
|
||||||
|
GrdDecodeSessionSwAVC *grd_decode_session_sw_avc_new (GError **error);
|
||||||
267
grd-decode-session.c
Normal file
267
grd-decode-session.c
Normal file
@@ -0,0 +1,267 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2025 Pascal Nowack
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License as
|
||||||
|
* published by the Free Software Foundation; either version 2 of the
|
||||||
|
* License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
|
||||||
|
* 02111-1307, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
|
#include "grd-decode-session.h"
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
GrdSampleBuffer *sample_buffer;
|
||||||
|
|
||||||
|
GrdDecodeSessionOnFrameReadyFunc callback;
|
||||||
|
gpointer callback_user_data;
|
||||||
|
} GrdDecodeFrameTask;
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
gboolean in_shutdown;
|
||||||
|
|
||||||
|
GThread *decode_thread;
|
||||||
|
GMainContext *decode_context;
|
||||||
|
|
||||||
|
GSource *frame_decode_source;
|
||||||
|
GAsyncQueue *task_queue;
|
||||||
|
} GrdDecodeSessionPrivate;
|
||||||
|
|
||||||
|
G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE (GrdDecodeSession, grd_decode_session,
|
||||||
|
G_TYPE_OBJECT)
|
||||||
|
|
||||||
|
void
|
||||||
|
grd_decode_session_get_drm_format_modifiers (GrdDecodeSession *decode_session,
|
||||||
|
uint32_t drm_format,
|
||||||
|
uint32_t *out_n_modifiers,
|
||||||
|
uint64_t **out_modifiers)
|
||||||
|
{
|
||||||
|
GrdDecodeSessionClass *klass = GRD_DECODE_SESSION_GET_CLASS (decode_session);
|
||||||
|
|
||||||
|
klass->get_drm_format_modifiers (decode_session, drm_format,
|
||||||
|
out_n_modifiers, out_modifiers);
|
||||||
|
}
|
||||||
|
|
||||||
|
gboolean
|
||||||
|
grd_decode_session_reset (GrdDecodeSession *decode_session,
|
||||||
|
uint32_t surface_width,
|
||||||
|
uint32_t surface_height,
|
||||||
|
uint64_t drm_format_modifier,
|
||||||
|
GError **error)
|
||||||
|
{
|
||||||
|
GrdDecodeSessionClass *klass = GRD_DECODE_SESSION_GET_CLASS (decode_session);
|
||||||
|
|
||||||
|
return klass->reset (decode_session, surface_width, surface_height,
|
||||||
|
drm_format_modifier, error);
|
||||||
|
}
|
||||||
|
|
||||||
|
gboolean
|
||||||
|
grd_decode_session_register_buffer (GrdDecodeSession *decode_session,
|
||||||
|
struct pw_buffer *pw_buffer,
|
||||||
|
GError **error)
|
||||||
|
{
|
||||||
|
GrdDecodeSessionClass *klass = GRD_DECODE_SESSION_GET_CLASS (decode_session);
|
||||||
|
|
||||||
|
return klass->register_buffer (decode_session, pw_buffer, error);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
grd_decode_session_unregister_buffer (GrdDecodeSession *decode_session,
|
||||||
|
struct pw_buffer *pw_buffer)
|
||||||
|
{
|
||||||
|
GrdDecodeSessionClass *klass = GRD_DECODE_SESSION_GET_CLASS (decode_session);
|
||||||
|
|
||||||
|
return klass->unregister_buffer (decode_session, pw_buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
GrdSampleBuffer *
|
||||||
|
grd_decode_session_get_sample_buffer (GrdDecodeSession *decode_session,
|
||||||
|
struct pw_buffer *pw_buffer)
|
||||||
|
{
|
||||||
|
GrdDecodeSessionClass *klass = GRD_DECODE_SESSION_GET_CLASS (decode_session);
|
||||||
|
|
||||||
|
return klass->get_sample_buffer (decode_session, pw_buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t
|
||||||
|
grd_decode_session_get_n_pending_frames (GrdDecodeSession *decode_session)
|
||||||
|
{
|
||||||
|
GrdDecodeSessionClass *klass = GRD_DECODE_SESSION_GET_CLASS (decode_session);
|
||||||
|
|
||||||
|
return klass->get_n_pending_frames (decode_session);
|
||||||
|
}
|
||||||
|
|
||||||
|
gboolean
|
||||||
|
grd_decode_session_decode_frame (GrdDecodeSession *decode_session,
|
||||||
|
GrdSampleBuffer *sample_buffer,
|
||||||
|
GrdDecodeSessionOnFrameReadyFunc on_frame_ready,
|
||||||
|
gpointer user_data,
|
||||||
|
GError **error)
|
||||||
|
{
|
||||||
|
GrdDecodeSessionClass *klass = GRD_DECODE_SESSION_GET_CLASS (decode_session);
|
||||||
|
GrdDecodeSessionPrivate *priv =
|
||||||
|
grd_decode_session_get_instance_private (decode_session);
|
||||||
|
GrdDecodeFrameTask *task;
|
||||||
|
|
||||||
|
if (!klass->submit_sample (decode_session, sample_buffer, error))
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
task = g_new0 (GrdDecodeFrameTask, 1);
|
||||||
|
task->sample_buffer = sample_buffer;
|
||||||
|
task->callback = on_frame_ready;
|
||||||
|
task->callback_user_data = user_data;
|
||||||
|
|
||||||
|
g_async_queue_push (priv->task_queue, task);
|
||||||
|
g_source_set_ready_time (priv->frame_decode_source, 0);
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
stop_decode_thread (GrdDecodeSession *decode_session)
|
||||||
|
{
|
||||||
|
GrdDecodeSessionPrivate *priv =
|
||||||
|
grd_decode_session_get_instance_private (decode_session);
|
||||||
|
|
||||||
|
g_assert (priv->decode_context);
|
||||||
|
g_assert (priv->decode_thread);
|
||||||
|
|
||||||
|
priv->in_shutdown = TRUE;
|
||||||
|
|
||||||
|
g_main_context_wakeup (priv->decode_context);
|
||||||
|
g_clear_pointer (&priv->decode_thread, g_thread_join);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
grd_decode_session_dispose (GObject *object)
|
||||||
|
{
|
||||||
|
GrdDecodeSession *decode_session = GRD_DECODE_SESSION (object);
|
||||||
|
GrdDecodeSessionPrivate *priv =
|
||||||
|
grd_decode_session_get_instance_private (decode_session);
|
||||||
|
|
||||||
|
g_assert (g_async_queue_try_pop (priv->task_queue) == NULL);
|
||||||
|
|
||||||
|
if (priv->decode_thread)
|
||||||
|
stop_decode_thread (decode_session);
|
||||||
|
|
||||||
|
if (priv->frame_decode_source)
|
||||||
|
{
|
||||||
|
g_source_destroy (priv->frame_decode_source);
|
||||||
|
g_clear_pointer (&priv->frame_decode_source, g_source_unref);
|
||||||
|
}
|
||||||
|
|
||||||
|
g_clear_pointer (&priv->decode_context, g_main_context_unref);
|
||||||
|
|
||||||
|
G_OBJECT_CLASS (grd_decode_session_parent_class)->dispose (object);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
grd_decode_session_finalize (GObject *object)
|
||||||
|
{
|
||||||
|
GrdDecodeSession *decode_session = GRD_DECODE_SESSION (object);
|
||||||
|
GrdDecodeSessionPrivate *priv =
|
||||||
|
grd_decode_session_get_instance_private (decode_session);
|
||||||
|
|
||||||
|
g_clear_pointer (&priv->task_queue, g_async_queue_unref);
|
||||||
|
|
||||||
|
G_OBJECT_CLASS (grd_decode_session_parent_class)->finalize (object);
|
||||||
|
}
|
||||||
|
|
||||||
|
static gpointer
|
||||||
|
decode_thread_func (gpointer data)
|
||||||
|
{
|
||||||
|
GrdDecodeSession *decode_session = data;
|
||||||
|
GrdDecodeSessionPrivate *priv =
|
||||||
|
grd_decode_session_get_instance_private (decode_session);
|
||||||
|
|
||||||
|
while (!priv->in_shutdown)
|
||||||
|
g_main_context_iteration (priv->decode_context, TRUE);
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
decode_frames (gpointer user_data)
|
||||||
|
{
|
||||||
|
GrdDecodeSession *decode_session = user_data;
|
||||||
|
GrdDecodeSessionClass *klass = GRD_DECODE_SESSION_GET_CLASS (decode_session);
|
||||||
|
GrdDecodeSessionPrivate *priv =
|
||||||
|
grd_decode_session_get_instance_private (decode_session);
|
||||||
|
GrdDecodeFrameTask *task;
|
||||||
|
|
||||||
|
while ((task = g_async_queue_try_pop (priv->task_queue)))
|
||||||
|
{
|
||||||
|
gboolean success;
|
||||||
|
g_autoptr (GError) error = NULL;
|
||||||
|
|
||||||
|
success = klass->decode_frame (decode_session, task->sample_buffer,
|
||||||
|
&error);
|
||||||
|
g_assert (success == !error);
|
||||||
|
|
||||||
|
task->callback (decode_session, task->sample_buffer,
|
||||||
|
task->callback_user_data, error);
|
||||||
|
|
||||||
|
g_free (task);
|
||||||
|
}
|
||||||
|
|
||||||
|
return G_SOURCE_CONTINUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
source_dispatch (GSource *source,
|
||||||
|
GSourceFunc callback,
|
||||||
|
gpointer user_data)
|
||||||
|
{
|
||||||
|
g_source_set_ready_time (source, -1);
|
||||||
|
|
||||||
|
return callback (user_data);
|
||||||
|
}
|
||||||
|
|
||||||
|
static GSourceFuncs source_funcs =
|
||||||
|
{
|
||||||
|
.dispatch = source_dispatch,
|
||||||
|
};
|
||||||
|
|
||||||
|
static void
|
||||||
|
grd_decode_session_init (GrdDecodeSession *decode_session)
|
||||||
|
{
|
||||||
|
GrdDecodeSessionPrivate *priv =
|
||||||
|
grd_decode_session_get_instance_private (decode_session);
|
||||||
|
GSource *frame_decode_source;
|
||||||
|
|
||||||
|
priv->task_queue = g_async_queue_new ();
|
||||||
|
|
||||||
|
priv->decode_context = g_main_context_new ();
|
||||||
|
priv->decode_thread = g_thread_new ("Decode thread",
|
||||||
|
decode_thread_func,
|
||||||
|
decode_session);
|
||||||
|
|
||||||
|
frame_decode_source = g_source_new (&source_funcs, sizeof (GSource));
|
||||||
|
g_source_set_callback (frame_decode_source, decode_frames,
|
||||||
|
decode_session, NULL);
|
||||||
|
g_source_set_ready_time (frame_decode_source, -1);
|
||||||
|
g_source_attach (frame_decode_source, priv->decode_context);
|
||||||
|
priv->frame_decode_source = frame_decode_source;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
grd_decode_session_class_init (GrdDecodeSessionClass *klass)
|
||||||
|
{
|
||||||
|
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
||||||
|
|
||||||
|
object_class->dispose = grd_decode_session_dispose;
|
||||||
|
object_class->finalize = grd_decode_session_finalize;
|
||||||
|
}
|
||||||
92
grd-decode-session.h
Normal file
92
grd-decode-session.h
Normal file
@@ -0,0 +1,92 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2025 Pascal Nowack
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License as
|
||||||
|
* published by the Free Software Foundation; either version 2 of the
|
||||||
|
* License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
|
||||||
|
* 02111-1307, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <glib-object.h>
|
||||||
|
#include <pipewire/pipewire.h>
|
||||||
|
|
||||||
|
#include "grd-types.h"
|
||||||
|
|
||||||
|
#define GRD_TYPE_DECODE_SESSION (grd_decode_session_get_type ())
|
||||||
|
G_DECLARE_DERIVABLE_TYPE (GrdDecodeSession, grd_decode_session,
|
||||||
|
GRD, DECODE_SESSION, GObject)
|
||||||
|
|
||||||
|
typedef void (* GrdDecodeSessionOnFrameReadyFunc) (GrdDecodeSession *decode_session,
|
||||||
|
GrdSampleBuffer *sample_buffer,
|
||||||
|
gpointer user_data,
|
||||||
|
GError *error);
|
||||||
|
|
||||||
|
struct _GrdDecodeSessionClass
|
||||||
|
{
|
||||||
|
GObjectClass parent_class;
|
||||||
|
|
||||||
|
void (* get_drm_format_modifiers) (GrdDecodeSession *decode_session,
|
||||||
|
uint32_t drm_format,
|
||||||
|
uint32_t *out_n_modifiers,
|
||||||
|
uint64_t **out_modifiers);
|
||||||
|
gboolean (* reset) (GrdDecodeSession *decode_session,
|
||||||
|
uint32_t surface_width,
|
||||||
|
uint32_t surface_height,
|
||||||
|
uint64_t drm_format_modifier,
|
||||||
|
GError **error);
|
||||||
|
gboolean (* register_buffer) (GrdDecodeSession *decode_session,
|
||||||
|
struct pw_buffer *pw_buffer,
|
||||||
|
GError **error);
|
||||||
|
void (* unregister_buffer) (GrdDecodeSession *decode_session,
|
||||||
|
struct pw_buffer *pw_buffer);
|
||||||
|
GrdSampleBuffer *(* get_sample_buffer) (GrdDecodeSession *decode_session,
|
||||||
|
struct pw_buffer *pw_buffer);
|
||||||
|
gboolean (* submit_sample) (GrdDecodeSession *decode_session,
|
||||||
|
GrdSampleBuffer *sample_buffer,
|
||||||
|
GError **error);
|
||||||
|
uint32_t (* get_n_pending_frames) (GrdDecodeSession *decode_session);
|
||||||
|
gboolean (* decode_frame) (GrdDecodeSession *decode_session,
|
||||||
|
GrdSampleBuffer *sample_buffer,
|
||||||
|
GError **error);
|
||||||
|
};
|
||||||
|
|
||||||
|
void grd_decode_session_get_drm_format_modifiers (GrdDecodeSession *decode_session,
|
||||||
|
uint32_t drm_format,
|
||||||
|
uint32_t *out_n_modifiers,
|
||||||
|
uint64_t **out_modifiers);
|
||||||
|
|
||||||
|
gboolean grd_decode_session_reset (GrdDecodeSession *decode_session,
|
||||||
|
uint32_t surface_width,
|
||||||
|
uint32_t surface_height,
|
||||||
|
uint64_t drm_format_modifier,
|
||||||
|
GError **error);
|
||||||
|
|
||||||
|
gboolean grd_decode_session_register_buffer (GrdDecodeSession *decode_session,
|
||||||
|
struct pw_buffer *pw_buffer,
|
||||||
|
GError **error);
|
||||||
|
|
||||||
|
void grd_decode_session_unregister_buffer (GrdDecodeSession *decode_session,
|
||||||
|
struct pw_buffer *pw_buffer);
|
||||||
|
|
||||||
|
GrdSampleBuffer *grd_decode_session_get_sample_buffer (GrdDecodeSession *decode_session,
|
||||||
|
struct pw_buffer *pw_buffer);
|
||||||
|
|
||||||
|
uint32_t grd_decode_session_get_n_pending_frames (GrdDecodeSession *decode_session);
|
||||||
|
|
||||||
|
gboolean grd_decode_session_decode_frame (GrdDecodeSession *decode_session,
|
||||||
|
GrdSampleBuffer *sample_buffer,
|
||||||
|
GrdDecodeSessionOnFrameReadyFunc on_frame_ready,
|
||||||
|
gpointer user_data,
|
||||||
|
GError **error);
|
||||||
1325
grd-egl-thread.c
Normal file
1325
grd-egl-thread.c
Normal file
File diff suppressed because it is too large
Load Diff
114
grd-egl-thread.h
Normal file
114
grd-egl-thread.h
Normal file
@@ -0,0 +1,114 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2021 Red Hat Inc.
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License as
|
||||||
|
* published by the Free Software Foundation; either version 2 of the
|
||||||
|
* License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
|
||||||
|
* 02111-1307, USA.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <glib.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#include "grd-types.h"
|
||||||
|
|
||||||
|
typedef gpointer GrdEglThreadSlot;
|
||||||
|
|
||||||
|
typedef void (* GrdEglThreadCallback) (gboolean success,
|
||||||
|
gpointer user_data);
|
||||||
|
typedef gboolean (* GrdEglThreadCustomFunc) (gpointer user_data);
|
||||||
|
typedef gboolean (* GrdEglThreadAllocBufferFunc) (gpointer user_data,
|
||||||
|
uint32_t pbo);
|
||||||
|
typedef void (* GrdEglThreadDeallocBufferFunc) (gpointer user_data);
|
||||||
|
|
||||||
|
GrdEglThread * grd_egl_thread_new (GError **error);
|
||||||
|
|
||||||
|
void grd_egl_thread_free (GrdEglThread *egl_thread);
|
||||||
|
|
||||||
|
const char * grd_egl_thread_get_drm_render_node (GrdEglThread *egl_thread);
|
||||||
|
|
||||||
|
void * grd_egl_thread_acquire_slot (GrdEglThread *egl_thread);
|
||||||
|
|
||||||
|
void grd_egl_thread_release_slot (GrdEglThread *egl_thread,
|
||||||
|
GrdEglThreadSlot slot);
|
||||||
|
|
||||||
|
void grd_egl_thread_download (GrdEglThread *egl_thread,
|
||||||
|
GrdEglThreadSlot slot,
|
||||||
|
uint8_t *dst_data,
|
||||||
|
int dst_row_width,
|
||||||
|
uint32_t format,
|
||||||
|
unsigned int width,
|
||||||
|
unsigned int height,
|
||||||
|
uint32_t n_planes,
|
||||||
|
const int *fds,
|
||||||
|
const uint32_t *strides,
|
||||||
|
const uint32_t *offsets,
|
||||||
|
const uint64_t *modifiers,
|
||||||
|
GrdEglThreadCallback callback,
|
||||||
|
gpointer user_data,
|
||||||
|
GDestroyNotify destroy);
|
||||||
|
|
||||||
|
void grd_egl_thread_allocate (GrdEglThread *egl_thread,
|
||||||
|
uint32_t height,
|
||||||
|
uint32_t stride,
|
||||||
|
GrdEglThreadAllocBufferFunc allocate_func,
|
||||||
|
gpointer allocate_user_data,
|
||||||
|
GrdEglThreadCallback callback,
|
||||||
|
gpointer user_data,
|
||||||
|
GDestroyNotify destroy);
|
||||||
|
|
||||||
|
void grd_egl_thread_deallocate (GrdEglThread *egl_thread,
|
||||||
|
uint32_t pbo,
|
||||||
|
GrdEglThreadDeallocBufferFunc deallocate_func,
|
||||||
|
gpointer deallocate_user_data,
|
||||||
|
GrdEglThreadCallback callback,
|
||||||
|
gpointer user_data,
|
||||||
|
GDestroyNotify destroy);
|
||||||
|
|
||||||
|
void grd_egl_thread_upload (GrdEglThread *egl_thread,
|
||||||
|
GrdEglThreadSlot slot,
|
||||||
|
uint32_t pbo,
|
||||||
|
uint32_t height,
|
||||||
|
uint32_t stride,
|
||||||
|
uint8_t *src_data,
|
||||||
|
GrdEglThreadAllocBufferFunc allocate_func,
|
||||||
|
gpointer allocate_user_data,
|
||||||
|
GDestroyNotify allocate_user_data_destroy,
|
||||||
|
GrdEglThreadCustomFunc realize_func,
|
||||||
|
gpointer realize_user_data,
|
||||||
|
GDestroyNotify realize_user_data_destroy,
|
||||||
|
GrdEglThreadCallback callback,
|
||||||
|
gpointer user_data,
|
||||||
|
GDestroyNotify destroy);
|
||||||
|
|
||||||
|
void grd_egl_thread_sync (GrdEglThread *egl_thread,
|
||||||
|
GrdEglThreadCallback callback,
|
||||||
|
gpointer user_data,
|
||||||
|
GDestroyNotify destroy);
|
||||||
|
|
||||||
|
void grd_egl_thread_run_custom_task (GrdEglThread *egl_thread,
|
||||||
|
GrdEglThreadCustomFunc custom_func,
|
||||||
|
gpointer custom_func_data,
|
||||||
|
GrdEglThreadCallback callback,
|
||||||
|
gpointer user_data,
|
||||||
|
GDestroyNotify destroy);
|
||||||
|
|
||||||
|
gboolean grd_egl_thread_get_modifiers_for_format (GrdEglThread *egl_thread,
|
||||||
|
uint32_t format,
|
||||||
|
int *out_n_modifiers,
|
||||||
|
uint64_t **out_modifiers);
|
||||||
|
|
||||||
|
G_DEFINE_AUTOPTR_CLEANUP_FUNC (GrdEglThread, grd_egl_thread_free)
|
||||||
261
grd-enable-service.c
Normal file
261
grd-enable-service.c
Normal file
@@ -0,0 +1,261 @@
|
|||||||
|
#include <config.h>
|
||||||
|
|
||||||
|
#include <gio/gio.h>
|
||||||
|
#include <limits.h>
|
||||||
|
#include <polkit/polkit.h>
|
||||||
|
#include <pwd.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
|
||||||
|
#include "grd-enums.h"
|
||||||
|
|
||||||
|
#define GRD_SYSTEMD_SERVICE "gnome-remote-desktop.service"
|
||||||
|
#define GRD_SYSTEMD_HEADLESS_SERVICE "gnome-remote-desktop-headless.service"
|
||||||
|
#define GRD_POLKIT_ACTION "org.gnome.remotedesktop.configure-system-daemon"
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
get_runtime_mode (const char *argv,
|
||||||
|
GrdRuntimeMode *runtime_mode)
|
||||||
|
{
|
||||||
|
if (g_str_equal (argv, "user"))
|
||||||
|
*runtime_mode = GRD_RUNTIME_MODE_SCREEN_SHARE;
|
||||||
|
else if (g_str_equal (argv, "headless"))
|
||||||
|
*runtime_mode = GRD_RUNTIME_MODE_HEADLESS;
|
||||||
|
else if (g_str_equal (argv, "system"))
|
||||||
|
*runtime_mode = GRD_RUNTIME_MODE_SYSTEM;
|
||||||
|
else
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
check_polkit_permission (const char *action_id,
|
||||||
|
pid_t pid,
|
||||||
|
GError **error)
|
||||||
|
{
|
||||||
|
g_autoptr (GPermission) permission = NULL;
|
||||||
|
g_autoptr (PolkitSubject) subject = NULL;
|
||||||
|
|
||||||
|
subject = polkit_unix_process_new_for_owner (pid, 0, -1);
|
||||||
|
permission = polkit_permission_new_sync (action_id, subject, NULL, error);
|
||||||
|
if (!permission)
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
if (!g_permission_get_allowed (permission))
|
||||||
|
{
|
||||||
|
g_set_error (error,
|
||||||
|
G_IO_ERROR,
|
||||||
|
G_IO_ERROR_PERMISSION_DENIED,
|
||||||
|
"This program requires callers to have access to the %s "
|
||||||
|
"polkit action", action_id);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
validate_caller (int64_t pid)
|
||||||
|
{
|
||||||
|
g_autoptr (GError) error = NULL;
|
||||||
|
|
||||||
|
if (geteuid () != 0)
|
||||||
|
{
|
||||||
|
g_printerr ("Enabling system service is not meant to be done directly by users.\n");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!check_polkit_permission (GRD_POLKIT_ACTION, (pid_t) pid, &error))
|
||||||
|
{
|
||||||
|
g_printerr ("Authorization failed for " GRD_POLKIT_ACTION ": %s\n",
|
||||||
|
error->message);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
get_systemd_unit_info (GrdRuntimeMode runtime_mode,
|
||||||
|
GBusType *bus_type,
|
||||||
|
const char **unit)
|
||||||
|
{
|
||||||
|
switch (runtime_mode)
|
||||||
|
{
|
||||||
|
case GRD_RUNTIME_MODE_HEADLESS:
|
||||||
|
*bus_type = G_BUS_TYPE_SESSION;
|
||||||
|
*unit = GRD_SYSTEMD_HEADLESS_SERVICE;
|
||||||
|
break;
|
||||||
|
case GRD_RUNTIME_MODE_SYSTEM:
|
||||||
|
*bus_type = G_BUS_TYPE_SYSTEM;
|
||||||
|
*unit = GRD_SYSTEMD_SERVICE;
|
||||||
|
break;
|
||||||
|
case GRD_RUNTIME_MODE_SCREEN_SHARE:
|
||||||
|
*bus_type = G_BUS_TYPE_SESSION;
|
||||||
|
*unit = GRD_SYSTEMD_SERVICE;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
g_assert_not_reached ();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
enable_systemd_unit (GrdRuntimeMode runtime_mode,
|
||||||
|
GError **error)
|
||||||
|
{
|
||||||
|
g_autoptr (GDBusConnection) connection = NULL;
|
||||||
|
g_autoptr (GVariant) reply = NULL;
|
||||||
|
GBusType bus_type;
|
||||||
|
const char *unit;
|
||||||
|
|
||||||
|
get_systemd_unit_info (runtime_mode, &bus_type, &unit);
|
||||||
|
|
||||||
|
connection = g_bus_get_sync (bus_type, NULL, error);
|
||||||
|
if (!connection)
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
reply = g_dbus_connection_call_sync (connection,
|
||||||
|
"org.freedesktop.systemd1",
|
||||||
|
"/org/freedesktop/systemd1",
|
||||||
|
"org.freedesktop.systemd1.Manager",
|
||||||
|
"StartUnit",
|
||||||
|
g_variant_new ("(ss)",
|
||||||
|
unit, "replace"),
|
||||||
|
NULL,
|
||||||
|
G_DBUS_CALL_FLAGS_NO_AUTO_START,
|
||||||
|
-1, NULL, error);
|
||||||
|
if (!reply)
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
reply = g_dbus_connection_call_sync (connection,
|
||||||
|
"org.freedesktop.systemd1",
|
||||||
|
"/org/freedesktop/systemd1",
|
||||||
|
"org.freedesktop.systemd1.Manager",
|
||||||
|
"EnableUnitFiles",
|
||||||
|
g_variant_new ("(^asbb)",
|
||||||
|
(const char*[])
|
||||||
|
{ unit, NULL },
|
||||||
|
FALSE, FALSE),
|
||||||
|
(GVariantType *) "(ba(sss))",
|
||||||
|
G_DBUS_CALL_FLAGS_NONE,
|
||||||
|
-1,
|
||||||
|
NULL,
|
||||||
|
error);
|
||||||
|
|
||||||
|
return reply != NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
disable_systemd_unit (GrdRuntimeMode runtime_mode,
|
||||||
|
GError **error)
|
||||||
|
{
|
||||||
|
g_autoptr (GDBusConnection) connection = NULL;
|
||||||
|
g_autoptr (GVariant) reply = NULL;
|
||||||
|
GBusType bus_type;
|
||||||
|
const char *unit;
|
||||||
|
|
||||||
|
get_systemd_unit_info (runtime_mode, &bus_type, &unit);
|
||||||
|
|
||||||
|
connection = g_bus_get_sync (bus_type, NULL, error);
|
||||||
|
if (!connection)
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
reply = g_dbus_connection_call_sync (connection,
|
||||||
|
"org.freedesktop.systemd1",
|
||||||
|
"/org/freedesktop/systemd1",
|
||||||
|
"org.freedesktop.systemd1.Manager",
|
||||||
|
"StopUnit",
|
||||||
|
g_variant_new ("(ss)",
|
||||||
|
unit, "replace"),
|
||||||
|
NULL,
|
||||||
|
G_DBUS_CALL_FLAGS_NO_AUTO_START,
|
||||||
|
-1, NULL, error);
|
||||||
|
if (!reply)
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
reply = g_dbus_connection_call_sync (connection,
|
||||||
|
"org.freedesktop.systemd1",
|
||||||
|
"/org/freedesktop/systemd1",
|
||||||
|
"org.freedesktop.systemd1.Manager",
|
||||||
|
"DisableUnitFiles",
|
||||||
|
g_variant_new ("(^asb)",
|
||||||
|
(const char*[])
|
||||||
|
{ unit, NULL },
|
||||||
|
FALSE),
|
||||||
|
(GVariantType *) "(a(sss))",
|
||||||
|
G_DBUS_CALL_FLAGS_NONE,
|
||||||
|
-1,
|
||||||
|
NULL,
|
||||||
|
error);
|
||||||
|
|
||||||
|
return reply != NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
print_usage (void)
|
||||||
|
{
|
||||||
|
g_printerr ("Usage: %s pid <user|headless|system> <true|false>\n", g_get_prgname ());
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
main (int argc,
|
||||||
|
char *argv[])
|
||||||
|
{
|
||||||
|
g_autoptr (GError) error = NULL;
|
||||||
|
GrdRuntimeMode runtime_mode;
|
||||||
|
gboolean success;
|
||||||
|
gboolean enable;
|
||||||
|
int64_t pid;
|
||||||
|
|
||||||
|
g_set_prgname (argv[0]);
|
||||||
|
|
||||||
|
if (argc != 4)
|
||||||
|
{
|
||||||
|
print_usage ();
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
success = g_ascii_string_to_signed (argv[1], 10, 1, INT_MAX, &pid, NULL);
|
||||||
|
if (!success)
|
||||||
|
{
|
||||||
|
print_usage ();
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
success = get_runtime_mode (argv[2], &runtime_mode);
|
||||||
|
if (!success)
|
||||||
|
{
|
||||||
|
print_usage ();
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
enable = g_strcmp0 (argv[3], "true") == 0;
|
||||||
|
if (!enable && g_strcmp0 (argv[3], "false") != 0)
|
||||||
|
{
|
||||||
|
print_usage ();
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (runtime_mode == GRD_RUNTIME_MODE_SYSTEM)
|
||||||
|
{
|
||||||
|
success = validate_caller (pid);
|
||||||
|
if (!success)
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (enable)
|
||||||
|
success = enable_systemd_unit (runtime_mode, &error);
|
||||||
|
else
|
||||||
|
success = disable_systemd_unit (runtime_mode, &error);
|
||||||
|
if (!success)
|
||||||
|
{
|
||||||
|
g_printerr ("Failed to %s service: %s\n",
|
||||||
|
enable ? "enable" : "disable", error->message);
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
||||||
56
grd-encode-context.c
Normal file
56
grd-encode-context.c
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2024 Pascal Nowack
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License as
|
||||||
|
* published by the Free Software Foundation; either version 2 of the
|
||||||
|
* License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
|
||||||
|
* 02111-1307, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
|
#include "grd-encode-context.h"
|
||||||
|
|
||||||
|
#include <glib.h>
|
||||||
|
|
||||||
|
struct _GrdEncodeContext
|
||||||
|
{
|
||||||
|
cairo_region_t *damage_region;
|
||||||
|
};
|
||||||
|
|
||||||
|
cairo_region_t *
|
||||||
|
grd_encode_context_get_damage_region (GrdEncodeContext *encode_context)
|
||||||
|
{
|
||||||
|
return encode_context->damage_region;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
grd_encode_context_set_damage_region (GrdEncodeContext *encode_context,
|
||||||
|
cairo_region_t *damage_region)
|
||||||
|
{
|
||||||
|
encode_context->damage_region = cairo_region_reference (damage_region);
|
||||||
|
}
|
||||||
|
|
||||||
|
GrdEncodeContext *
|
||||||
|
grd_encode_context_new (void)
|
||||||
|
{
|
||||||
|
return g_new0 (GrdEncodeContext, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
grd_encode_context_free (GrdEncodeContext *encode_context)
|
||||||
|
{
|
||||||
|
g_clear_pointer (&encode_context->damage_region, cairo_region_destroy);
|
||||||
|
|
||||||
|
g_free (encode_context);
|
||||||
|
}
|
||||||
33
grd-encode-context.h
Normal file
33
grd-encode-context.h
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2024 Pascal Nowack
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License as
|
||||||
|
* published by the Free Software Foundation; either version 2 of the
|
||||||
|
* License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
|
||||||
|
* 02111-1307, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <cairo/cairo.h>
|
||||||
|
|
||||||
|
#include "grd-types.h"
|
||||||
|
|
||||||
|
GrdEncodeContext *grd_encode_context_new (void);
|
||||||
|
|
||||||
|
void grd_encode_context_free (GrdEncodeContext *encode_context);
|
||||||
|
|
||||||
|
cairo_region_t *grd_encode_context_get_damage_region (GrdEncodeContext *encode_context);
|
||||||
|
|
||||||
|
void grd_encode_context_set_damage_region (GrdEncodeContext *encode_context,
|
||||||
|
cairo_region_t *damage_region);
|
||||||
406
grd-encode-session-ca-sw.c
Normal file
406
grd-encode-session-ca-sw.c
Normal file
@@ -0,0 +1,406 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2024 Pascal Nowack
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License as
|
||||||
|
* published by the Free Software Foundation; either version 2 of the
|
||||||
|
* License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
|
||||||
|
* 02111-1307, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
|
#include "grd-encode-session-ca-sw.h"
|
||||||
|
|
||||||
|
#include <gio/gio.h>
|
||||||
|
|
||||||
|
#include "grd-bitstream.h"
|
||||||
|
#include "grd-encode-context.h"
|
||||||
|
#include "grd-image-view-rgb.h"
|
||||||
|
#include "grd-local-buffer.h"
|
||||||
|
#include "grd-rdp-sw-encoder-ca.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* One surface is needed, when encoding a frame,
|
||||||
|
* one is needed, when preparing the view,
|
||||||
|
* one is needed for the submitted pending frame in the renderer,
|
||||||
|
* and one is needed to prepare a new pending frame before it is submitted to
|
||||||
|
* the renderer, where it then replaces the old pending frame
|
||||||
|
*
|
||||||
|
* In total, this makes four needed source surfaces
|
||||||
|
*/
|
||||||
|
#define N_SRC_SURFACES 4
|
||||||
|
|
||||||
|
/*
|
||||||
|
* One encode stream is needed, when submitting a frame,
|
||||||
|
* one is needed, when encoding a frame
|
||||||
|
*
|
||||||
|
* In total, this makes two needed encode streams
|
||||||
|
*/
|
||||||
|
#define N_ENCODE_STREAMS 2
|
||||||
|
|
||||||
|
#define INITIAL_STREAM_SIZE 16384
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
GrdImageView *image_view;
|
||||||
|
} RGBSurface;
|
||||||
|
|
||||||
|
struct _GrdEncodeSessionCaSw
|
||||||
|
{
|
||||||
|
GrdEncodeSession parent;
|
||||||
|
|
||||||
|
GrdRdpSwEncoderCa *encoder_ca;
|
||||||
|
|
||||||
|
uint32_t surface_width;
|
||||||
|
uint32_t surface_height;
|
||||||
|
|
||||||
|
GHashTable *surfaces;
|
||||||
|
|
||||||
|
GMutex pending_encodes_mutex;
|
||||||
|
GHashTable *pending_encodes;
|
||||||
|
|
||||||
|
GMutex acquired_encode_streams_mutex;
|
||||||
|
wStream *encode_streams[N_ENCODE_STREAMS];
|
||||||
|
GHashTable *acquired_encode_streams;
|
||||||
|
|
||||||
|
GMutex bitstreams_mutex;
|
||||||
|
GHashTable *bitstreams;
|
||||||
|
|
||||||
|
gboolean pending_header;
|
||||||
|
};
|
||||||
|
|
||||||
|
G_DEFINE_TYPE (GrdEncodeSessionCaSw, grd_encode_session_ca_sw,
|
||||||
|
GRD_TYPE_ENCODE_SESSION)
|
||||||
|
|
||||||
|
static RGBSurface *
|
||||||
|
rgb_surface_new (void)
|
||||||
|
{
|
||||||
|
RGBSurface *rgb_surface = NULL;
|
||||||
|
GrdImageViewRGB *image_view_rgb;
|
||||||
|
|
||||||
|
rgb_surface = g_new0 (RGBSurface, 1);
|
||||||
|
|
||||||
|
image_view_rgb = grd_image_view_rgb_new ();
|
||||||
|
rgb_surface->image_view = GRD_IMAGE_VIEW (image_view_rgb);
|
||||||
|
|
||||||
|
return rgb_surface;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
rgb_surface_free (RGBSurface *rgb_surface)
|
||||||
|
{
|
||||||
|
g_clear_object (&rgb_surface->image_view);
|
||||||
|
|
||||||
|
g_free (rgb_surface);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
grd_encode_session_ca_sw_get_surface_size (GrdEncodeSession *encode_session,
|
||||||
|
uint32_t *surface_width,
|
||||||
|
uint32_t *surface_height)
|
||||||
|
{
|
||||||
|
g_assert_not_reached ();
|
||||||
|
}
|
||||||
|
|
||||||
|
static GList *
|
||||||
|
grd_encode_session_ca_sw_get_image_views (GrdEncodeSession *encode_session)
|
||||||
|
{
|
||||||
|
GrdEncodeSessionCaSw *encode_session_ca =
|
||||||
|
GRD_ENCODE_SESSION_CA_SW (encode_session);
|
||||||
|
|
||||||
|
return g_hash_table_get_keys (encode_session_ca->surfaces);
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
grd_encode_session_ca_sw_has_pending_frames (GrdEncodeSession *encode_session)
|
||||||
|
{
|
||||||
|
GrdEncodeSessionCaSw *encode_session_ca =
|
||||||
|
GRD_ENCODE_SESSION_CA_SW (encode_session);
|
||||||
|
|
||||||
|
g_mutex_lock (&encode_session_ca->pending_encodes_mutex);
|
||||||
|
g_assert (g_hash_table_size (encode_session_ca->pending_encodes) == 0);
|
||||||
|
g_mutex_unlock (&encode_session_ca->pending_encodes_mutex);
|
||||||
|
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
grd_encode_session_ca_sw_encode_frame (GrdEncodeSession *encode_session,
|
||||||
|
GrdEncodeContext *encode_context,
|
||||||
|
GrdImageView *image_view,
|
||||||
|
GError **error)
|
||||||
|
{
|
||||||
|
GrdEncodeSessionCaSw *encode_session_ca =
|
||||||
|
GRD_ENCODE_SESSION_CA_SW (encode_session);
|
||||||
|
g_autoptr (GMutexLocker) locker = NULL;
|
||||||
|
|
||||||
|
locker = g_mutex_locker_new (&encode_session_ca->pending_encodes_mutex);
|
||||||
|
g_assert (!g_hash_table_contains (encode_session_ca->pending_encodes,
|
||||||
|
image_view));
|
||||||
|
|
||||||
|
g_hash_table_insert (encode_session_ca->pending_encodes,
|
||||||
|
image_view, encode_context);
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static wStream *
|
||||||
|
acquire_encode_stream (GrdEncodeSessionCaSw *encode_session_ca)
|
||||||
|
{
|
||||||
|
g_autoptr (GMutexLocker) locker = NULL;
|
||||||
|
uint32_t i;
|
||||||
|
|
||||||
|
locker = g_mutex_locker_new (&encode_session_ca->acquired_encode_streams_mutex);
|
||||||
|
for (i = 0; i < N_ENCODE_STREAMS; ++i)
|
||||||
|
{
|
||||||
|
wStream *encode_stream = encode_session_ca->encode_streams[i];
|
||||||
|
|
||||||
|
if (g_hash_table_contains (encode_session_ca->acquired_encode_streams,
|
||||||
|
encode_stream))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
g_hash_table_add (encode_session_ca->acquired_encode_streams,
|
||||||
|
encode_stream);
|
||||||
|
return encode_stream;
|
||||||
|
}
|
||||||
|
|
||||||
|
g_assert_not_reached ();
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
release_encode_stream (GrdEncodeSessionCaSw *encode_session_ca,
|
||||||
|
wStream *encode_stream)
|
||||||
|
{
|
||||||
|
g_autoptr (GMutexLocker) locker = NULL;
|
||||||
|
|
||||||
|
locker = g_mutex_locker_new (&encode_session_ca->acquired_encode_streams_mutex);
|
||||||
|
if (!g_hash_table_remove (encode_session_ca->acquired_encode_streams,
|
||||||
|
encode_stream))
|
||||||
|
g_assert_not_reached ();
|
||||||
|
}
|
||||||
|
|
||||||
|
static GrdBitstream *
|
||||||
|
grd_encode_session_ca_sw_lock_bitstream (GrdEncodeSession *encode_session,
|
||||||
|
GrdImageView *image_view,
|
||||||
|
GError **error)
|
||||||
|
{
|
||||||
|
GrdEncodeSessionCaSw *encode_session_ca =
|
||||||
|
GRD_ENCODE_SESSION_CA_SW (encode_session);
|
||||||
|
GrdImageViewRGB *image_view_rgb = GRD_IMAGE_VIEW_RGB (image_view);
|
||||||
|
GrdEncodeContext *encode_context = NULL;
|
||||||
|
g_autoptr (GMutexLocker) locker = NULL;
|
||||||
|
GrdLocalBuffer *local_buffer;
|
||||||
|
uint8_t *buffer;
|
||||||
|
uint32_t buffer_stride;
|
||||||
|
cairo_region_t *damage_region;
|
||||||
|
wStream *encode_stream;
|
||||||
|
GrdBitstream *bitstream;
|
||||||
|
|
||||||
|
locker = g_mutex_locker_new (&encode_session_ca->pending_encodes_mutex);
|
||||||
|
if (!g_hash_table_lookup_extended (encode_session_ca->pending_encodes,
|
||||||
|
image_view,
|
||||||
|
NULL, (gpointer *) &encode_context))
|
||||||
|
g_assert_not_reached ();
|
||||||
|
|
||||||
|
g_clear_pointer (&locker, g_mutex_locker_free);
|
||||||
|
g_assert (encode_context);
|
||||||
|
|
||||||
|
local_buffer = grd_image_view_rgb_get_local_buffer (image_view_rgb);
|
||||||
|
buffer = grd_local_buffer_get_buffer (local_buffer);
|
||||||
|
buffer_stride = grd_local_buffer_get_buffer_stride (local_buffer);
|
||||||
|
damage_region = grd_encode_context_get_damage_region (encode_context);
|
||||||
|
encode_stream = acquire_encode_stream (encode_session_ca);
|
||||||
|
|
||||||
|
grd_rdp_sw_encoder_ca_encode_progressive_frame (encode_session_ca->encoder_ca,
|
||||||
|
encode_session_ca->surface_width,
|
||||||
|
encode_session_ca->surface_height,
|
||||||
|
buffer,
|
||||||
|
buffer_stride,
|
||||||
|
damage_region,
|
||||||
|
encode_stream,
|
||||||
|
encode_session_ca->pending_header);
|
||||||
|
|
||||||
|
encode_session_ca->pending_header = FALSE;
|
||||||
|
|
||||||
|
bitstream = grd_bitstream_new (Stream_Buffer (encode_stream),
|
||||||
|
Stream_Length (encode_stream));
|
||||||
|
|
||||||
|
g_mutex_lock (&encode_session_ca->bitstreams_mutex);
|
||||||
|
g_hash_table_insert (encode_session_ca->bitstreams, bitstream, encode_stream);
|
||||||
|
g_mutex_unlock (&encode_session_ca->bitstreams_mutex);
|
||||||
|
|
||||||
|
locker = g_mutex_locker_new (&encode_session_ca->pending_encodes_mutex);
|
||||||
|
if (!g_hash_table_remove (encode_session_ca->pending_encodes, image_view))
|
||||||
|
g_assert_not_reached ();
|
||||||
|
|
||||||
|
return bitstream;
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
grd_encode_session_ca_sw_unlock_bitstream (GrdEncodeSession *encode_session,
|
||||||
|
GrdBitstream *bitstream,
|
||||||
|
GError **error)
|
||||||
|
{
|
||||||
|
GrdEncodeSessionCaSw *encode_session_ca =
|
||||||
|
GRD_ENCODE_SESSION_CA_SW (encode_session);
|
||||||
|
wStream *encode_stream = NULL;
|
||||||
|
g_autoptr (GMutexLocker) locker = NULL;
|
||||||
|
|
||||||
|
locker = g_mutex_locker_new (&encode_session_ca->bitstreams_mutex);
|
||||||
|
if (!g_hash_table_steal_extended (encode_session_ca->bitstreams,
|
||||||
|
bitstream,
|
||||||
|
NULL, (gpointer *) &encode_stream))
|
||||||
|
g_assert_not_reached ();
|
||||||
|
|
||||||
|
g_clear_pointer (&locker, g_mutex_locker_free);
|
||||||
|
g_assert (encode_stream);
|
||||||
|
|
||||||
|
grd_bitstream_free (bitstream);
|
||||||
|
release_encode_stream (encode_session_ca, encode_stream);
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
init_surfaces_and_streams (GrdEncodeSessionCaSw *encode_session_ca,
|
||||||
|
GError **error)
|
||||||
|
{
|
||||||
|
uint32_t i;
|
||||||
|
|
||||||
|
for (i = 0; i < N_SRC_SURFACES; ++i)
|
||||||
|
{
|
||||||
|
RGBSurface *surface;
|
||||||
|
|
||||||
|
surface = rgb_surface_new ();
|
||||||
|
|
||||||
|
g_hash_table_insert (encode_session_ca->surfaces,
|
||||||
|
surface->image_view, surface);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < N_ENCODE_STREAMS; ++i)
|
||||||
|
{
|
||||||
|
wStream *encode_stream;
|
||||||
|
|
||||||
|
encode_stream = Stream_New (NULL, INITIAL_STREAM_SIZE);
|
||||||
|
if (!encode_stream)
|
||||||
|
{
|
||||||
|
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
|
||||||
|
"Failed to create encode stream");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
encode_session_ca->encode_streams[i] = encode_stream;
|
||||||
|
}
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
GrdEncodeSessionCaSw *
|
||||||
|
grd_encode_session_ca_sw_new (GrdRdpSwEncoderCa *encoder_ca,
|
||||||
|
uint32_t source_width,
|
||||||
|
uint32_t source_height,
|
||||||
|
GError **error)
|
||||||
|
{
|
||||||
|
g_autoptr (GrdEncodeSessionCaSw) encode_session_ca = NULL;
|
||||||
|
|
||||||
|
encode_session_ca = g_object_new (GRD_TYPE_ENCODE_SESSION_CA_SW, NULL);
|
||||||
|
encode_session_ca->encoder_ca = encoder_ca;
|
||||||
|
encode_session_ca->surface_width = source_width;
|
||||||
|
encode_session_ca->surface_height = source_height;
|
||||||
|
|
||||||
|
if (!init_surfaces_and_streams (encode_session_ca, error))
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
return g_steal_pointer (&encode_session_ca);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
encode_stream_free (wStream *encode_stream)
|
||||||
|
{
|
||||||
|
Stream_Free (encode_stream, TRUE);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
grd_encode_session_ca_sw_dispose (GObject *object)
|
||||||
|
{
|
||||||
|
GrdEncodeSessionCaSw *encode_session_ca = GRD_ENCODE_SESSION_CA_SW (object);
|
||||||
|
uint32_t i;
|
||||||
|
|
||||||
|
g_assert (g_hash_table_size (encode_session_ca->pending_encodes) == 0);
|
||||||
|
g_assert (g_hash_table_size (encode_session_ca->acquired_encode_streams) == 0);
|
||||||
|
g_assert (g_hash_table_size (encode_session_ca->bitstreams) == 0);
|
||||||
|
|
||||||
|
for (i = 0; i < N_ENCODE_STREAMS; ++i)
|
||||||
|
g_clear_pointer (&encode_session_ca->encode_streams[i], encode_stream_free);
|
||||||
|
|
||||||
|
g_clear_pointer (&encode_session_ca->surfaces, g_hash_table_unref);
|
||||||
|
|
||||||
|
G_OBJECT_CLASS (grd_encode_session_ca_sw_parent_class)->dispose (object);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
grd_encode_session_ca_sw_finalize (GObject *object)
|
||||||
|
{
|
||||||
|
GrdEncodeSessionCaSw *encode_session_ca = GRD_ENCODE_SESSION_CA_SW (object);
|
||||||
|
|
||||||
|
g_mutex_clear (&encode_session_ca->bitstreams_mutex);
|
||||||
|
g_mutex_clear (&encode_session_ca->acquired_encode_streams_mutex);
|
||||||
|
g_mutex_clear (&encode_session_ca->pending_encodes_mutex);
|
||||||
|
|
||||||
|
g_clear_pointer (&encode_session_ca->bitstreams, g_hash_table_unref);
|
||||||
|
g_clear_pointer (&encode_session_ca->acquired_encode_streams, g_hash_table_unref);
|
||||||
|
g_clear_pointer (&encode_session_ca->pending_encodes, g_hash_table_unref);
|
||||||
|
|
||||||
|
G_OBJECT_CLASS (grd_encode_session_ca_sw_parent_class)->finalize (object);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
grd_encode_session_ca_sw_init (GrdEncodeSessionCaSw *encode_session_ca)
|
||||||
|
{
|
||||||
|
encode_session_ca->pending_header = TRUE;
|
||||||
|
|
||||||
|
encode_session_ca->surfaces =
|
||||||
|
g_hash_table_new_full (NULL, NULL,
|
||||||
|
NULL, (GDestroyNotify) rgb_surface_free);
|
||||||
|
encode_session_ca->pending_encodes = g_hash_table_new (NULL, NULL);
|
||||||
|
encode_session_ca->acquired_encode_streams = g_hash_table_new (NULL, NULL);
|
||||||
|
encode_session_ca->bitstreams = g_hash_table_new (NULL, NULL);
|
||||||
|
|
||||||
|
g_mutex_init (&encode_session_ca->pending_encodes_mutex);
|
||||||
|
g_mutex_init (&encode_session_ca->acquired_encode_streams_mutex);
|
||||||
|
g_mutex_init (&encode_session_ca->bitstreams_mutex);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
grd_encode_session_ca_sw_class_init (GrdEncodeSessionCaSwClass *klass)
|
||||||
|
{
|
||||||
|
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
||||||
|
GrdEncodeSessionClass *encode_session_class =
|
||||||
|
GRD_ENCODE_SESSION_CLASS (klass);
|
||||||
|
|
||||||
|
object_class->dispose = grd_encode_session_ca_sw_dispose;
|
||||||
|
object_class->finalize = grd_encode_session_ca_sw_finalize;
|
||||||
|
|
||||||
|
encode_session_class->get_surface_size =
|
||||||
|
grd_encode_session_ca_sw_get_surface_size;
|
||||||
|
encode_session_class->get_image_views =
|
||||||
|
grd_encode_session_ca_sw_get_image_views;
|
||||||
|
encode_session_class->has_pending_frames =
|
||||||
|
grd_encode_session_ca_sw_has_pending_frames;
|
||||||
|
encode_session_class->encode_frame =
|
||||||
|
grd_encode_session_ca_sw_encode_frame;
|
||||||
|
encode_session_class->lock_bitstream =
|
||||||
|
grd_encode_session_ca_sw_lock_bitstream;
|
||||||
|
encode_session_class->unlock_bitstream =
|
||||||
|
grd_encode_session_ca_sw_unlock_bitstream;
|
||||||
|
}
|
||||||
32
grd-encode-session-ca-sw.h
Normal file
32
grd-encode-session-ca-sw.h
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2024 Pascal Nowack
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License as
|
||||||
|
* published by the Free Software Foundation; either version 2 of the
|
||||||
|
* License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
|
||||||
|
* 02111-1307, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "grd-encode-session.h"
|
||||||
|
#include "grd-types.h"
|
||||||
|
|
||||||
|
#define GRD_TYPE_ENCODE_SESSION_CA_SW (grd_encode_session_ca_sw_get_type ())
|
||||||
|
G_DECLARE_FINAL_TYPE (GrdEncodeSessionCaSw, grd_encode_session_ca_sw,
|
||||||
|
GRD, ENCODE_SESSION_CA_SW, GrdEncodeSession)
|
||||||
|
|
||||||
|
GrdEncodeSessionCaSw *grd_encode_session_ca_sw_new (GrdRdpSwEncoderCa *encoder_ca,
|
||||||
|
uint32_t source_width,
|
||||||
|
uint32_t source_height,
|
||||||
|
GError **error);
|
||||||
1790
grd-encode-session-vaapi.c
Normal file
1790
grd-encode-session-vaapi.c
Normal file
File diff suppressed because it is too large
Load Diff
36
grd-encode-session-vaapi.h
Normal file
36
grd-encode-session-vaapi.h
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2024 Pascal Nowack
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License as
|
||||||
|
* published by the Free Software Foundation; either version 2 of the
|
||||||
|
* License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
|
||||||
|
* 02111-1307, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <va/va.h>
|
||||||
|
|
||||||
|
#include "grd-encode-session.h"
|
||||||
|
|
||||||
|
#define GRD_TYPE_ENCODE_SESSION_VAAPI (grd_encode_session_vaapi_get_type ())
|
||||||
|
G_DECLARE_FINAL_TYPE (GrdEncodeSessionVaapi, grd_encode_session_vaapi,
|
||||||
|
GRD, ENCODE_SESSION_VAAPI, GrdEncodeSession)
|
||||||
|
|
||||||
|
GrdEncodeSessionVaapi *grd_encode_session_vaapi_new (GrdVkDevice *vk_device,
|
||||||
|
VADisplay va_display,
|
||||||
|
gboolean supports_quality_level,
|
||||||
|
uint32_t source_width,
|
||||||
|
uint32_t source_height,
|
||||||
|
uint32_t refresh_rate,
|
||||||
|
GError **error);
|
||||||
244
grd-encode-session.c
Normal file
244
grd-encode-session.c
Normal file
@@ -0,0 +1,244 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2024 Pascal Nowack
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License as
|
||||||
|
* published by the Free Software Foundation; either version 2 of the
|
||||||
|
* License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
|
||||||
|
* 02111-1307, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
|
#include "grd-encode-session.h"
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
GrdImageView *image_view;
|
||||||
|
|
||||||
|
GrdEncodeSessionOnBitstreamLockedFunc callback;
|
||||||
|
gpointer callback_user_data;
|
||||||
|
} GrdLockBitstreamTask;
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
gboolean in_shutdown;
|
||||||
|
|
||||||
|
GThread *encode_thread;
|
||||||
|
GMainContext *encode_context;
|
||||||
|
|
||||||
|
GSource *bitstream_lock_source;
|
||||||
|
GAsyncQueue *task_queue;
|
||||||
|
} GrdEncodeSessionPrivate;
|
||||||
|
|
||||||
|
G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE (GrdEncodeSession, grd_encode_session,
|
||||||
|
G_TYPE_OBJECT)
|
||||||
|
|
||||||
|
void
|
||||||
|
grd_encode_session_get_surface_size (GrdEncodeSession *encode_session,
|
||||||
|
uint32_t *surface_width,
|
||||||
|
uint32_t *surface_height)
|
||||||
|
{
|
||||||
|
GrdEncodeSessionClass *klass = GRD_ENCODE_SESSION_GET_CLASS (encode_session);
|
||||||
|
|
||||||
|
return klass->get_surface_size (encode_session, surface_width, surface_height);
|
||||||
|
}
|
||||||
|
|
||||||
|
GList *
|
||||||
|
grd_encode_session_get_image_views (GrdEncodeSession *encode_session)
|
||||||
|
{
|
||||||
|
GrdEncodeSessionClass *klass = GRD_ENCODE_SESSION_GET_CLASS (encode_session);
|
||||||
|
|
||||||
|
return klass->get_image_views (encode_session);
|
||||||
|
}
|
||||||
|
|
||||||
|
gboolean
|
||||||
|
grd_encode_session_has_pending_frames (GrdEncodeSession *encode_session)
|
||||||
|
{
|
||||||
|
GrdEncodeSessionClass *klass = GRD_ENCODE_SESSION_GET_CLASS (encode_session);
|
||||||
|
|
||||||
|
return klass->has_pending_frames (encode_session);
|
||||||
|
}
|
||||||
|
|
||||||
|
gboolean
|
||||||
|
grd_encode_session_encode_frame (GrdEncodeSession *encode_session,
|
||||||
|
GrdEncodeContext *encode_context,
|
||||||
|
GrdImageView *image_view,
|
||||||
|
GError **error)
|
||||||
|
{
|
||||||
|
GrdEncodeSessionClass *klass = GRD_ENCODE_SESSION_GET_CLASS (encode_session);
|
||||||
|
|
||||||
|
return klass->encode_frame (encode_session, encode_context, image_view, error);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
grd_encode_session_lock_bitstream (GrdEncodeSession *encode_session,
|
||||||
|
GrdImageView *image_view,
|
||||||
|
GrdEncodeSessionOnBitstreamLockedFunc on_bitstream_locked,
|
||||||
|
gpointer user_data)
|
||||||
|
{
|
||||||
|
GrdEncodeSessionPrivate *priv =
|
||||||
|
grd_encode_session_get_instance_private (encode_session);
|
||||||
|
GrdLockBitstreamTask *task;
|
||||||
|
|
||||||
|
task = g_new0 (GrdLockBitstreamTask, 1);
|
||||||
|
task->image_view = image_view;
|
||||||
|
task->callback = on_bitstream_locked;
|
||||||
|
task->callback_user_data = user_data;
|
||||||
|
|
||||||
|
g_async_queue_push (priv->task_queue, task);
|
||||||
|
g_source_set_ready_time (priv->bitstream_lock_source, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
gboolean
|
||||||
|
grd_encode_session_unlock_bitstream (GrdEncodeSession *encode_session,
|
||||||
|
GrdBitstream *bitstream,
|
||||||
|
GError **error)
|
||||||
|
{
|
||||||
|
GrdEncodeSessionClass *klass = GRD_ENCODE_SESSION_GET_CLASS (encode_session);
|
||||||
|
|
||||||
|
return klass->unlock_bitstream (encode_session, bitstream, error);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
stop_encode_thread (GrdEncodeSession *encode_session)
|
||||||
|
{
|
||||||
|
GrdEncodeSessionPrivate *priv =
|
||||||
|
grd_encode_session_get_instance_private (encode_session);
|
||||||
|
|
||||||
|
g_assert (priv->encode_context);
|
||||||
|
g_assert (priv->encode_thread);
|
||||||
|
|
||||||
|
priv->in_shutdown = TRUE;
|
||||||
|
|
||||||
|
g_main_context_wakeup (priv->encode_context);
|
||||||
|
g_clear_pointer (&priv->encode_thread, g_thread_join);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
grd_encode_session_dispose (GObject *object)
|
||||||
|
{
|
||||||
|
GrdEncodeSession *encode_session = GRD_ENCODE_SESSION (object);
|
||||||
|
GrdEncodeSessionPrivate *priv =
|
||||||
|
grd_encode_session_get_instance_private (encode_session);
|
||||||
|
|
||||||
|
g_assert (g_async_queue_try_pop (priv->task_queue) == NULL);
|
||||||
|
|
||||||
|
if (priv->encode_thread)
|
||||||
|
stop_encode_thread (encode_session);
|
||||||
|
|
||||||
|
if (priv->bitstream_lock_source)
|
||||||
|
{
|
||||||
|
g_source_destroy (priv->bitstream_lock_source);
|
||||||
|
g_clear_pointer (&priv->bitstream_lock_source, g_source_unref);
|
||||||
|
}
|
||||||
|
|
||||||
|
g_clear_pointer (&priv->encode_context, g_main_context_unref);
|
||||||
|
|
||||||
|
G_OBJECT_CLASS (grd_encode_session_parent_class)->dispose (object);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
grd_encode_session_finalize (GObject *object)
|
||||||
|
{
|
||||||
|
GrdEncodeSession *encode_session = GRD_ENCODE_SESSION (object);
|
||||||
|
GrdEncodeSessionPrivate *priv =
|
||||||
|
grd_encode_session_get_instance_private (encode_session);
|
||||||
|
|
||||||
|
g_clear_pointer (&priv->task_queue, g_async_queue_unref);
|
||||||
|
|
||||||
|
G_OBJECT_CLASS (grd_encode_session_parent_class)->finalize (object);
|
||||||
|
}
|
||||||
|
|
||||||
|
static gpointer
|
||||||
|
encode_thread_func (gpointer data)
|
||||||
|
{
|
||||||
|
GrdEncodeSession *encode_session = data;
|
||||||
|
GrdEncodeSessionPrivate *priv =
|
||||||
|
grd_encode_session_get_instance_private (encode_session);
|
||||||
|
|
||||||
|
while (!priv->in_shutdown)
|
||||||
|
g_main_context_iteration (priv->encode_context, TRUE);
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
lock_bitstreams (gpointer user_data)
|
||||||
|
{
|
||||||
|
GrdEncodeSession *encode_session = user_data;
|
||||||
|
GrdEncodeSessionClass *klass = GRD_ENCODE_SESSION_GET_CLASS (encode_session);
|
||||||
|
GrdEncodeSessionPrivate *priv =
|
||||||
|
grd_encode_session_get_instance_private (encode_session);
|
||||||
|
GrdLockBitstreamTask *task;
|
||||||
|
|
||||||
|
while ((task = g_async_queue_try_pop (priv->task_queue)))
|
||||||
|
{
|
||||||
|
GrdBitstream *bitstream;
|
||||||
|
g_autoptr (GError) error = NULL;
|
||||||
|
|
||||||
|
bitstream = klass->lock_bitstream (encode_session, task->image_view,
|
||||||
|
&error);
|
||||||
|
task->callback (encode_session, bitstream, task->callback_user_data,
|
||||||
|
error);
|
||||||
|
|
||||||
|
g_free (task);
|
||||||
|
}
|
||||||
|
|
||||||
|
return G_SOURCE_CONTINUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
source_dispatch (GSource *source,
|
||||||
|
GSourceFunc callback,
|
||||||
|
gpointer user_data)
|
||||||
|
{
|
||||||
|
g_source_set_ready_time (source, -1);
|
||||||
|
|
||||||
|
return callback (user_data);
|
||||||
|
}
|
||||||
|
|
||||||
|
static GSourceFuncs source_funcs =
|
||||||
|
{
|
||||||
|
.dispatch = source_dispatch,
|
||||||
|
};
|
||||||
|
|
||||||
|
static void
|
||||||
|
grd_encode_session_init (GrdEncodeSession *encode_session)
|
||||||
|
{
|
||||||
|
GrdEncodeSessionPrivate *priv =
|
||||||
|
grd_encode_session_get_instance_private (encode_session);
|
||||||
|
GSource *bitstream_lock_source;
|
||||||
|
|
||||||
|
priv->task_queue = g_async_queue_new ();
|
||||||
|
|
||||||
|
priv->encode_context = g_main_context_new ();
|
||||||
|
priv->encode_thread = g_thread_new ("Encode thread",
|
||||||
|
encode_thread_func,
|
||||||
|
encode_session);
|
||||||
|
|
||||||
|
bitstream_lock_source = g_source_new (&source_funcs, sizeof (GSource));
|
||||||
|
g_source_set_callback (bitstream_lock_source, lock_bitstreams,
|
||||||
|
encode_session, NULL);
|
||||||
|
g_source_set_ready_time (bitstream_lock_source, -1);
|
||||||
|
g_source_attach (bitstream_lock_source, priv->encode_context);
|
||||||
|
priv->bitstream_lock_source = bitstream_lock_source;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
grd_encode_session_class_init (GrdEncodeSessionClass *klass)
|
||||||
|
{
|
||||||
|
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
||||||
|
|
||||||
|
object_class->dispose = grd_encode_session_dispose;
|
||||||
|
object_class->finalize = grd_encode_session_finalize;
|
||||||
|
}
|
||||||
77
grd-encode-session.h
Normal file
77
grd-encode-session.h
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2024 Pascal Nowack
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License as
|
||||||
|
* published by the Free Software Foundation; either version 2 of the
|
||||||
|
* License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
|
||||||
|
* 02111-1307, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <glib-object.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#include "grd-types.h"
|
||||||
|
|
||||||
|
#define GRD_TYPE_ENCODE_SESSION (grd_encode_session_get_type ())
|
||||||
|
G_DECLARE_DERIVABLE_TYPE (GrdEncodeSession, grd_encode_session,
|
||||||
|
GRD, ENCODE_SESSION, GObject)
|
||||||
|
|
||||||
|
typedef void (* GrdEncodeSessionOnBitstreamLockedFunc) (GrdEncodeSession *encode_session,
|
||||||
|
GrdBitstream *bitstream,
|
||||||
|
gpointer user_data,
|
||||||
|
GError *error);
|
||||||
|
|
||||||
|
struct _GrdEncodeSessionClass
|
||||||
|
{
|
||||||
|
GObjectClass parent_class;
|
||||||
|
|
||||||
|
void (* get_surface_size) (GrdEncodeSession *encode_session,
|
||||||
|
uint32_t *surface_width,
|
||||||
|
uint32_t *surface_height);
|
||||||
|
GList *(* get_image_views) (GrdEncodeSession *encode_session);
|
||||||
|
gboolean (* has_pending_frames) (GrdEncodeSession *encode_session);
|
||||||
|
gboolean (* encode_frame) (GrdEncodeSession *encode_session,
|
||||||
|
GrdEncodeContext *encode_context,
|
||||||
|
GrdImageView *image_view,
|
||||||
|
GError **error);
|
||||||
|
GrdBitstream *(* lock_bitstream) (GrdEncodeSession *encode_session,
|
||||||
|
GrdImageView *image_view,
|
||||||
|
GError **error);
|
||||||
|
gboolean (* unlock_bitstream) (GrdEncodeSession *encode_session,
|
||||||
|
GrdBitstream *bitstream,
|
||||||
|
GError **error);
|
||||||
|
};
|
||||||
|
|
||||||
|
void grd_encode_session_get_surface_size (GrdEncodeSession *encode_session,
|
||||||
|
uint32_t *surface_width,
|
||||||
|
uint32_t *surface_height);
|
||||||
|
|
||||||
|
GList *grd_encode_session_get_image_views (GrdEncodeSession *encode_session);
|
||||||
|
|
||||||
|
gboolean grd_encode_session_has_pending_frames (GrdEncodeSession *encode_session);
|
||||||
|
|
||||||
|
gboolean grd_encode_session_encode_frame (GrdEncodeSession *encode_session,
|
||||||
|
GrdEncodeContext *encode_context,
|
||||||
|
GrdImageView *image_view,
|
||||||
|
GError **error);
|
||||||
|
|
||||||
|
void grd_encode_session_lock_bitstream (GrdEncodeSession *encode_session,
|
||||||
|
GrdImageView *image_view,
|
||||||
|
GrdEncodeSessionOnBitstreamLockedFunc on_bitstream_locked,
|
||||||
|
gpointer user_data);
|
||||||
|
|
||||||
|
gboolean grd_encode_session_unlock_bitstream (GrdEncodeSession *encode_session,
|
||||||
|
GrdBitstream *bitstream,
|
||||||
|
GError **error);
|
||||||
41
grd-enum-types.c.in
Normal file
41
grd-enum-types.c.in
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
/*** BEGIN file-header ***/
|
||||||
|
|
||||||
|
#include <glib-object.h>
|
||||||
|
|
||||||
|
/*** END file-header ***/
|
||||||
|
|
||||||
|
/*** BEGIN file-production ***/
|
||||||
|
|
||||||
|
/* enumerations from "@filename@" */
|
||||||
|
#include "@filename@"
|
||||||
|
|
||||||
|
/*** END file-production ***/
|
||||||
|
|
||||||
|
/*** BEGIN value-header ***/
|
||||||
|
GType
|
||||||
|
@enum_name@_get_type (void)
|
||||||
|
{
|
||||||
|
static size_t g_enum_type_id = 0;
|
||||||
|
|
||||||
|
if (g_once_init_enter (&g_enum_type_id))
|
||||||
|
{
|
||||||
|
static const G@Type@Value values[] = {
|
||||||
|
/*** END value-header ***/
|
||||||
|
|
||||||
|
/*** BEGIN value-production ***/
|
||||||
|
{ @VALUENAME@, "@VALUENAME@", "@valuenick@" },
|
||||||
|
/*** END value-production ***/
|
||||||
|
|
||||||
|
/*** BEGIN value-tail ***/
|
||||||
|
{ 0, NULL, NULL }
|
||||||
|
};
|
||||||
|
GType id;
|
||||||
|
|
||||||
|
id = g_@type@_register_static (g_intern_static_string ("@EnumName@"), values);
|
||||||
|
|
||||||
|
g_once_init_leave (&g_enum_type_id, id);
|
||||||
|
}
|
||||||
|
|
||||||
|
return g_enum_type_id;
|
||||||
|
}
|
||||||
|
/*** END value-tail ***/
|
||||||
23
grd-enum-types.h.in
Normal file
23
grd-enum-types.h.in
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
/*** BEGIN file-header ***/
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <glib-object.h>
|
||||||
|
|
||||||
|
G_BEGIN_DECLS
|
||||||
|
|
||||||
|
/*** END file-header ***/
|
||||||
|
|
||||||
|
/*** BEGIN file-production ***/
|
||||||
|
/* enumerations from "@basename@" */
|
||||||
|
/*** END file-production ***/
|
||||||
|
|
||||||
|
/*** BEGIN file-tail ***/
|
||||||
|
G_END_DECLS
|
||||||
|
|
||||||
|
/*** END file-tail ***/
|
||||||
|
|
||||||
|
/*** BEGIN value-header ***/
|
||||||
|
GType @enum_name@_get_type (void) G_GNUC_CONST;
|
||||||
|
#define @ENUMPREFIX@_TYPE_@ENUMSHORT@ (@enum_name@_get_type ())
|
||||||
|
|
||||||
|
/*** END value-header ***/
|
||||||
53
grd-enums.h
Normal file
53
grd-enums.h
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2018 Red Hat Inc.
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License as
|
||||||
|
* published by the Free Software Foundation; either version 2 of the
|
||||||
|
* License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
|
||||||
|
* 02111-1307, USA.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
typedef enum
|
||||||
|
{
|
||||||
|
GRD_RDP_SCREEN_SHARE_MODE_MIRROR_PRIMARY,
|
||||||
|
GRD_RDP_SCREEN_SHARE_MODE_EXTEND,
|
||||||
|
} GrdRdpScreenShareMode;
|
||||||
|
|
||||||
|
typedef enum
|
||||||
|
{
|
||||||
|
GRD_VNC_AUTH_METHOD_PROMPT,
|
||||||
|
GRD_VNC_AUTH_METHOD_PASSWORD
|
||||||
|
} GrdVncAuthMethod;
|
||||||
|
|
||||||
|
typedef enum
|
||||||
|
{
|
||||||
|
GRD_VNC_SCREEN_SHARE_MODE_MIRROR_PRIMARY,
|
||||||
|
GRD_VNC_SCREEN_SHARE_MODE_EXTEND,
|
||||||
|
} GrdVncScreenShareMode;
|
||||||
|
|
||||||
|
typedef enum
|
||||||
|
{
|
||||||
|
GRD_RUNTIME_MODE_SCREEN_SHARE,
|
||||||
|
GRD_RUNTIME_MODE_HEADLESS,
|
||||||
|
GRD_RUNTIME_MODE_SYSTEM,
|
||||||
|
GRD_RUNTIME_MODE_HANDOVER,
|
||||||
|
} GrdRuntimeMode;
|
||||||
|
|
||||||
|
typedef enum
|
||||||
|
{
|
||||||
|
GRD_RDP_AUTH_METHOD_CREDENTIALS = 1 << 0,
|
||||||
|
GRD_RDP_AUTH_METHOD_KERBEROS = 1 << 1,
|
||||||
|
} GrdRdpAuthMethods;
|
||||||
229
grd-frame-clock.c
Normal file
229
grd-frame-clock.c
Normal file
@@ -0,0 +1,229 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2025 Pascal Nowack
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License as
|
||||||
|
* published by the Free Software Foundation; either version 2 of the
|
||||||
|
* License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
|
||||||
|
* 02111-1307, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
|
#include "grd-frame-clock.h"
|
||||||
|
|
||||||
|
#include <gio/gio.h>
|
||||||
|
#include <glib/gstdio.h>
|
||||||
|
#include <sys/timerfd.h>
|
||||||
|
|
||||||
|
#include "grd-utils.h"
|
||||||
|
|
||||||
|
struct _GrdFrameClock
|
||||||
|
{
|
||||||
|
GObject parent;
|
||||||
|
|
||||||
|
GrdFrameClockCallback on_trigger;
|
||||||
|
gpointer on_trigger_user_data;
|
||||||
|
|
||||||
|
GSource *timer_source;
|
||||||
|
int timer_fd;
|
||||||
|
|
||||||
|
uint64_t start_time_ns;
|
||||||
|
uint64_t interval_ns;
|
||||||
|
gboolean armed;
|
||||||
|
};
|
||||||
|
|
||||||
|
G_DEFINE_TYPE (GrdFrameClock, grd_frame_clock,
|
||||||
|
G_TYPE_OBJECT)
|
||||||
|
|
||||||
|
gboolean
|
||||||
|
grd_frame_clock_is_armed (GrdFrameClock *frame_clock)
|
||||||
|
{
|
||||||
|
return frame_clock->armed;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline uint64_t
|
||||||
|
timespec_to_ns (struct timespec *spec)
|
||||||
|
{
|
||||||
|
return s2ns (spec->tv_sec) + spec->tv_nsec;
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint64_t
|
||||||
|
extrapolate_next_interval_boundary (uint64_t boundary_ns,
|
||||||
|
uint64_t reference_ns,
|
||||||
|
uint64_t interval_ns)
|
||||||
|
{
|
||||||
|
uint64_t num_intervals;
|
||||||
|
|
||||||
|
num_intervals = MAX ((reference_ns - boundary_ns + interval_ns - 1) /
|
||||||
|
interval_ns, 0);
|
||||||
|
return boundary_ns + num_intervals * interval_ns;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
schedule_next_frame (GrdFrameClock *frame_clock)
|
||||||
|
{
|
||||||
|
struct itimerspec timer_spec = {};
|
||||||
|
struct timespec now = {};
|
||||||
|
uint64_t next_dispatch_time_ns;
|
||||||
|
|
||||||
|
clock_gettime (CLOCK_MONOTONIC, &now);
|
||||||
|
|
||||||
|
next_dispatch_time_ns =
|
||||||
|
extrapolate_next_interval_boundary (frame_clock->start_time_ns,
|
||||||
|
timespec_to_ns (&now),
|
||||||
|
frame_clock->interval_ns);
|
||||||
|
|
||||||
|
timer_spec.it_value.tv_sec = next_dispatch_time_ns / s2ns (1);
|
||||||
|
timer_spec.it_value.tv_nsec = next_dispatch_time_ns % s2ns (1);
|
||||||
|
|
||||||
|
timerfd_settime (frame_clock->timer_fd, TFD_TIMER_ABSTIME, &timer_spec, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
grd_frame_clock_arm_timer (GrdFrameClock *frame_clock,
|
||||||
|
uint64_t clock_rate_num,
|
||||||
|
uint64_t clock_rate_denom)
|
||||||
|
{
|
||||||
|
struct timespec start_time = {};
|
||||||
|
|
||||||
|
clock_gettime (CLOCK_MONOTONIC, &start_time);
|
||||||
|
frame_clock->start_time_ns = timespec_to_ns (&start_time);
|
||||||
|
frame_clock->interval_ns = s2ns (clock_rate_denom) / clock_rate_num;
|
||||||
|
|
||||||
|
schedule_next_frame (frame_clock);
|
||||||
|
frame_clock->armed = TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
grd_frame_clock_disarm_timer (GrdFrameClock *frame_clock)
|
||||||
|
{
|
||||||
|
struct itimerspec timer_spec = {};
|
||||||
|
|
||||||
|
timerfd_settime (frame_clock->timer_fd, 0, &timer_spec, NULL);
|
||||||
|
frame_clock->armed = FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
timer_source_prepare (GSource *source,
|
||||||
|
int *timeout)
|
||||||
|
{
|
||||||
|
*timeout = -1;
|
||||||
|
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
timer_source_dispatch (GSource *source,
|
||||||
|
GSourceFunc callback,
|
||||||
|
gpointer user_data)
|
||||||
|
{
|
||||||
|
g_source_set_ready_time (source, -1);
|
||||||
|
|
||||||
|
return callback (user_data);
|
||||||
|
}
|
||||||
|
|
||||||
|
static GSourceFuncs timer_source_funcs =
|
||||||
|
{
|
||||||
|
.prepare = timer_source_prepare,
|
||||||
|
.dispatch = timer_source_dispatch,
|
||||||
|
};
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
handle_triggered_timer (gpointer user_data)
|
||||||
|
{
|
||||||
|
GrdFrameClock *frame_clock = user_data;
|
||||||
|
uint64_t expirations;
|
||||||
|
ssize_t ret;
|
||||||
|
|
||||||
|
ret = read (frame_clock->timer_fd, &expirations, sizeof (uint64_t));
|
||||||
|
if (ret == -1)
|
||||||
|
{
|
||||||
|
g_warning ("Failed to read from timerfd: %s", g_strerror (errno));
|
||||||
|
return G_SOURCE_CONTINUE;
|
||||||
|
}
|
||||||
|
else if (ret != sizeof (expirations))
|
||||||
|
{
|
||||||
|
g_warning ("Failed to read from timerfd: unexpected size %zi", ret);
|
||||||
|
return G_SOURCE_CONTINUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
frame_clock->on_trigger (frame_clock->on_trigger_user_data);
|
||||||
|
|
||||||
|
g_assert (frame_clock->armed);
|
||||||
|
schedule_next_frame (frame_clock);
|
||||||
|
|
||||||
|
return G_SOURCE_CONTINUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
GrdFrameClock *
|
||||||
|
grd_frame_clock_new (GMainContext *main_context,
|
||||||
|
GrdFrameClockCallback on_trigger,
|
||||||
|
gpointer on_trigger_user_data,
|
||||||
|
GError **error)
|
||||||
|
{
|
||||||
|
g_autoptr (GrdFrameClock) frame_clock = NULL;
|
||||||
|
|
||||||
|
frame_clock = g_object_new (GRD_TYPE_FRAME_CLOCK, NULL);
|
||||||
|
frame_clock->on_trigger = on_trigger;
|
||||||
|
frame_clock->on_trigger_user_data = on_trigger_user_data;
|
||||||
|
|
||||||
|
frame_clock->timer_fd = timerfd_create (CLOCK_MONOTONIC,
|
||||||
|
TFD_NONBLOCK | TFD_CLOEXEC);
|
||||||
|
if (frame_clock->timer_fd == -1)
|
||||||
|
{
|
||||||
|
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
|
||||||
|
"Failed to create timerfd: %s", g_strerror (errno));
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
frame_clock->timer_source = g_source_new (&timer_source_funcs,
|
||||||
|
sizeof (GSource));
|
||||||
|
g_source_set_callback (frame_clock->timer_source, handle_triggered_timer,
|
||||||
|
frame_clock, NULL);
|
||||||
|
g_source_set_ready_time (frame_clock->timer_source, -1);
|
||||||
|
g_source_add_unix_fd (frame_clock->timer_source,
|
||||||
|
frame_clock->timer_fd, G_IO_IN);
|
||||||
|
g_source_attach (frame_clock->timer_source, main_context);
|
||||||
|
|
||||||
|
return g_steal_pointer (&frame_clock);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
grd_frame_clock_dispose (GObject *object)
|
||||||
|
{
|
||||||
|
GrdFrameClock *frame_clock = GRD_FRAME_CLOCK (object);
|
||||||
|
|
||||||
|
if (frame_clock->timer_source)
|
||||||
|
{
|
||||||
|
g_source_destroy (frame_clock->timer_source);
|
||||||
|
g_clear_pointer (&frame_clock->timer_source, g_source_unref);
|
||||||
|
}
|
||||||
|
|
||||||
|
g_clear_fd (&frame_clock->timer_fd, NULL);
|
||||||
|
|
||||||
|
G_OBJECT_CLASS (grd_frame_clock_parent_class)->dispose (object);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
grd_frame_clock_init (GrdFrameClock *frame_clock)
|
||||||
|
{
|
||||||
|
frame_clock->timer_fd = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
grd_frame_clock_class_init (GrdFrameClockClass *klass)
|
||||||
|
{
|
||||||
|
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
||||||
|
|
||||||
|
object_class->dispose = grd_frame_clock_dispose;
|
||||||
|
}
|
||||||
42
grd-frame-clock.h
Normal file
42
grd-frame-clock.h
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2025 Pascal Nowack
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License as
|
||||||
|
* published by the Free Software Foundation; either version 2 of the
|
||||||
|
* License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
|
||||||
|
* 02111-1307, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <glib-object.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#define GRD_TYPE_FRAME_CLOCK (grd_frame_clock_get_type ())
|
||||||
|
G_DECLARE_FINAL_TYPE (GrdFrameClock, grd_frame_clock,
|
||||||
|
GRD, FRAME_CLOCK, GObject)
|
||||||
|
|
||||||
|
typedef void (* GrdFrameClockCallback) (gpointer user_data);
|
||||||
|
|
||||||
|
GrdFrameClock *grd_frame_clock_new (GMainContext *main_context,
|
||||||
|
GrdFrameClockCallback on_trigger,
|
||||||
|
gpointer on_trigger_user_data,
|
||||||
|
GError **error);
|
||||||
|
|
||||||
|
gboolean grd_frame_clock_is_armed (GrdFrameClock *frame_clock);
|
||||||
|
|
||||||
|
void grd_frame_clock_arm_timer (GrdFrameClock *frame_clock,
|
||||||
|
uint64_t clock_rate_num,
|
||||||
|
uint64_t clock_rate_denom);
|
||||||
|
|
||||||
|
void grd_frame_clock_disarm_timer (GrdFrameClock *frame_clock);
|
||||||
1017
grd-hwaccel-nvidia.c
Normal file
1017
grd-hwaccel-nvidia.c
Normal file
File diff suppressed because it is too large
Load Diff
102
grd-hwaccel-nvidia.h
Normal file
102
grd-hwaccel-nvidia.h
Normal file
@@ -0,0 +1,102 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2021 Pascal Nowack
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License as
|
||||||
|
* published by the Free Software Foundation; either version 2 of the
|
||||||
|
* License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
|
||||||
|
* 02111-1307, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <ffnvcodec/dynlink_cuda.h>
|
||||||
|
#include <glib-object.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#include "grd-types.h"
|
||||||
|
|
||||||
|
#define GRD_TYPE_HWACCEL_NVIDIA (grd_hwaccel_nvidia_get_type ())
|
||||||
|
G_DECLARE_FINAL_TYPE (GrdHwAccelNvidia, grd_hwaccel_nvidia,
|
||||||
|
GRD, HWACCEL_NVIDIA, GObject)
|
||||||
|
|
||||||
|
GrdHwAccelNvidia *grd_hwaccel_nvidia_new (GrdEglThread *egl_thread);
|
||||||
|
|
||||||
|
void grd_hwaccel_nvidia_get_cuda_functions (GrdHwAccelNvidia *hwaccel_nvidia,
|
||||||
|
gpointer *cuda_funcs);
|
||||||
|
|
||||||
|
void grd_hwaccel_nvidia_get_cuda_damage_kernels (GrdHwAccelNvidia *hwaccel_nvidia,
|
||||||
|
CUfunction *cu_chk_dmg_pxl,
|
||||||
|
CUfunction *cu_cmb_dmg_arr_cols,
|
||||||
|
CUfunction *cu_cmb_dmg_arr_rows,
|
||||||
|
CUfunction *cu_simplify_dmg_arr);
|
||||||
|
|
||||||
|
void grd_hwaccel_nvidia_push_cuda_context (GrdHwAccelNvidia *hwaccel_nvidia);
|
||||||
|
|
||||||
|
void grd_hwaccel_nvidia_pop_cuda_context (GrdHwAccelNvidia *hwaccel_nvidia);
|
||||||
|
|
||||||
|
gboolean grd_hwaccel_nvidia_register_read_only_gl_buffer (GrdHwAccelNvidia *hwaccel_nvidia,
|
||||||
|
CUgraphicsResource *cuda_resource,
|
||||||
|
uint32_t buffer);
|
||||||
|
|
||||||
|
void grd_hwaccel_nvidia_unregister_cuda_resource (GrdHwAccelNvidia *hwaccel_nvidia,
|
||||||
|
CUgraphicsResource cuda_resource,
|
||||||
|
CUstream cuda_stream);
|
||||||
|
|
||||||
|
gboolean grd_hwaccel_nvidia_map_cuda_resource (GrdHwAccelNvidia *hwaccel_nvidia,
|
||||||
|
CUgraphicsResource cuda_resource,
|
||||||
|
CUdeviceptr *dev_ptr,
|
||||||
|
size_t *size,
|
||||||
|
CUstream cuda_stream);
|
||||||
|
|
||||||
|
void grd_hwaccel_nvidia_unmap_cuda_resource (GrdHwAccelNvidia *hwaccel_nvidia,
|
||||||
|
CUgraphicsResource cuda_resource,
|
||||||
|
CUstream cuda_stream);
|
||||||
|
|
||||||
|
gboolean grd_hwaccel_nvidia_create_cuda_stream (GrdHwAccelNvidia *hwaccel_nvidia,
|
||||||
|
CUstream *cuda_stream);
|
||||||
|
|
||||||
|
void grd_hwaccel_nvidia_destroy_cuda_stream (GrdHwAccelNvidia *hwaccel_nvidia,
|
||||||
|
CUstream cuda_stream);
|
||||||
|
|
||||||
|
gboolean grd_hwaccel_nvidia_alloc_mem (GrdHwAccelNvidia *hwaccel_nvidia,
|
||||||
|
CUdeviceptr *device_ptr,
|
||||||
|
size_t size);
|
||||||
|
|
||||||
|
void grd_hwaccel_nvidia_clear_mem_ptr (GrdHwAccelNvidia *hwaccel_nvidia,
|
||||||
|
CUdeviceptr *device_ptr);
|
||||||
|
|
||||||
|
gboolean grd_hwaccel_nvidia_create_nvenc_session (GrdHwAccelNvidia *hwaccel_nvidia,
|
||||||
|
uint32_t *encode_session_id,
|
||||||
|
uint16_t surface_width,
|
||||||
|
uint16_t surface_height,
|
||||||
|
uint16_t *aligned_width,
|
||||||
|
uint16_t *aligned_height,
|
||||||
|
uint16_t refresh_rate);
|
||||||
|
|
||||||
|
void grd_hwaccel_nvidia_free_nvenc_session (GrdHwAccelNvidia *hwaccel_nvidia,
|
||||||
|
uint32_t encode_session_id);
|
||||||
|
|
||||||
|
gboolean grd_hwaccel_nvidia_avc420_encode_bgrx_frame (GrdHwAccelNvidia *hwaccel_nvidia,
|
||||||
|
uint32_t encode_session_id,
|
||||||
|
CUdeviceptr src_data,
|
||||||
|
CUdeviceptr *main_view_nv12,
|
||||||
|
uint16_t src_width,
|
||||||
|
uint16_t src_height,
|
||||||
|
uint16_t aligned_width,
|
||||||
|
uint16_t aligned_height,
|
||||||
|
CUstream cuda_stream);
|
||||||
|
|
||||||
|
gboolean grd_hwaccel_nvidia_avc420_retrieve_bitstream (GrdHwAccelNvidia *hwaccel_nvidia,
|
||||||
|
uint32_t encode_session_id,
|
||||||
|
uint8_t **bitstream,
|
||||||
|
uint32_t *bitstream_size);
|
||||||
323
grd-hwaccel-vaapi.c
Normal file
323
grd-hwaccel-vaapi.c
Normal file
@@ -0,0 +1,323 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2022 Pascal Nowack
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License as
|
||||||
|
* published by the Free Software Foundation; either version 2 of the
|
||||||
|
* License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
|
||||||
|
* 02111-1307, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
|
#include "grd-hwaccel-vaapi.h"
|
||||||
|
|
||||||
|
#include <inttypes.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <gio/gio.h>
|
||||||
|
#include <glib/gstdio.h>
|
||||||
|
#include <va/va.h>
|
||||||
|
#include <va/va_drm.h>
|
||||||
|
|
||||||
|
#include "grd-encode-session-vaapi.h"
|
||||||
|
#include "grd-vk-device.h"
|
||||||
|
|
||||||
|
struct _GrdHwAccelVaapi
|
||||||
|
{
|
||||||
|
GObject parent;
|
||||||
|
|
||||||
|
GrdVkDevice *vk_device;
|
||||||
|
|
||||||
|
int drm_fd;
|
||||||
|
VADisplay va_display;
|
||||||
|
|
||||||
|
gboolean supports_quality_level;
|
||||||
|
};
|
||||||
|
|
||||||
|
G_DEFINE_TYPE (GrdHwAccelVaapi, grd_hwaccel_vaapi, G_TYPE_OBJECT)
|
||||||
|
|
||||||
|
GrdEncodeSession *
|
||||||
|
grd_hwaccel_vaapi_create_encode_session (GrdHwAccelVaapi *hwaccel_vaapi,
|
||||||
|
uint32_t source_width,
|
||||||
|
uint32_t source_height,
|
||||||
|
uint32_t refresh_rate,
|
||||||
|
GError **error)
|
||||||
|
{
|
||||||
|
return (GrdEncodeSession *) grd_encode_session_vaapi_new (hwaccel_vaapi->vk_device,
|
||||||
|
hwaccel_vaapi->va_display,
|
||||||
|
hwaccel_vaapi->supports_quality_level,
|
||||||
|
source_width, source_height,
|
||||||
|
refresh_rate, error);
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
initialize_vaapi (GrdHwAccelVaapi *hwaccel_vaapi,
|
||||||
|
int *version_major,
|
||||||
|
int *version_minor,
|
||||||
|
int64_t render_minor,
|
||||||
|
GError **error)
|
||||||
|
{
|
||||||
|
g_autofree char *render_node = NULL;
|
||||||
|
VAStatus va_status;
|
||||||
|
|
||||||
|
render_node = g_strdup_printf ("/dev/dri/renderD%" PRIi64, render_minor);
|
||||||
|
|
||||||
|
hwaccel_vaapi->drm_fd = g_open (render_node, O_RDWR | O_CLOEXEC, 600);
|
||||||
|
if (hwaccel_vaapi->drm_fd == -1)
|
||||||
|
{
|
||||||
|
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
|
||||||
|
"Failed to open render node: %s", g_strerror (errno));
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
hwaccel_vaapi->va_display = vaGetDisplayDRM (hwaccel_vaapi->drm_fd);
|
||||||
|
if (!hwaccel_vaapi->va_display)
|
||||||
|
{
|
||||||
|
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
|
||||||
|
"Failed to get VA display from DRM fd");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
va_status = vaInitialize (hwaccel_vaapi->va_display,
|
||||||
|
version_major, version_minor);
|
||||||
|
if (va_status != VA_STATUS_SUCCESS)
|
||||||
|
{
|
||||||
|
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
|
||||||
|
"Failed to initialize VA display: %s", vaErrorStr (va_status));
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
has_device_avc_profile (VAProfile *profiles,
|
||||||
|
int n_profiles)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < n_profiles; ++i)
|
||||||
|
{
|
||||||
|
if (profiles[i] == VAProfileH264High)
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
has_device_avc_enc_entrypoint (VAEntrypoint *entrypoints,
|
||||||
|
int n_entrypoints)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < n_entrypoints; ++i)
|
||||||
|
{
|
||||||
|
if (entrypoints[i] == VAEntrypointEncSlice)
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
check_device_capabilities (GrdHwAccelVaapi *hwaccel_vaapi,
|
||||||
|
GError **error)
|
||||||
|
{
|
||||||
|
g_autofree VAProfile *profiles = NULL;
|
||||||
|
g_autofree VAEntrypoint *entrypoints = NULL;
|
||||||
|
VAConfigAttrib attributes[] =
|
||||||
|
{
|
||||||
|
{ .type = VAConfigAttribRTFormat },
|
||||||
|
{ .type = VAConfigAttribRateControl },
|
||||||
|
{ .type = VAConfigAttribEncMaxRefFrames },
|
||||||
|
{ .type = VAConfigAttribEncPackedHeaders },
|
||||||
|
{ .type = VAConfigAttribEncQualityRange },
|
||||||
|
};
|
||||||
|
int max_profiles;
|
||||||
|
int max_num_entrypoints;
|
||||||
|
int n_profiles = 0;
|
||||||
|
int n_entrypoints = 0;
|
||||||
|
uint32_t max_ref_l0;
|
||||||
|
VAStatus va_status;
|
||||||
|
|
||||||
|
max_profiles = vaMaxNumProfiles (hwaccel_vaapi->va_display);
|
||||||
|
if (max_profiles < 1)
|
||||||
|
{
|
||||||
|
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
|
||||||
|
"Invalid max num profiles: %i", max_profiles);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
profiles = g_new0 (VAProfile, max_profiles);
|
||||||
|
|
||||||
|
va_status = vaQueryConfigProfiles (hwaccel_vaapi->va_display,
|
||||||
|
profiles, &n_profiles);
|
||||||
|
if (va_status != VA_STATUS_SUCCESS)
|
||||||
|
{
|
||||||
|
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
|
||||||
|
"Failed to query config profiles: %s",
|
||||||
|
vaErrorStr (va_status));
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!has_device_avc_profile (profiles, n_profiles))
|
||||||
|
{
|
||||||
|
g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
|
||||||
|
"Unsuitable device, missing required AVC profile");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
max_num_entrypoints = vaMaxNumEntrypoints (hwaccel_vaapi->va_display);
|
||||||
|
if (max_num_entrypoints < 1)
|
||||||
|
{
|
||||||
|
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
|
||||||
|
"Invalid max num entrypoints: %i", max_num_entrypoints);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
entrypoints = g_new0 (VAEntrypoint, max_num_entrypoints);
|
||||||
|
|
||||||
|
va_status = vaQueryConfigEntrypoints (hwaccel_vaapi->va_display,
|
||||||
|
VAProfileH264High,
|
||||||
|
entrypoints, &n_entrypoints);
|
||||||
|
if (va_status != VA_STATUS_SUCCESS)
|
||||||
|
{
|
||||||
|
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
|
||||||
|
"Failed to query config entrypoints: %s",
|
||||||
|
vaErrorStr (va_status));
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!has_device_avc_enc_entrypoint (entrypoints, n_entrypoints))
|
||||||
|
{
|
||||||
|
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
|
||||||
|
"Unsuitable device, missing required AVC encoding "
|
||||||
|
"entrypoint");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
va_status = vaGetConfigAttributes (hwaccel_vaapi->va_display,
|
||||||
|
VAProfileH264High, VAEntrypointEncSlice,
|
||||||
|
attributes, G_N_ELEMENTS (attributes));
|
||||||
|
if (va_status != VA_STATUS_SUCCESS)
|
||||||
|
{
|
||||||
|
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
|
||||||
|
"Failed to get config attributes: %s",
|
||||||
|
vaErrorStr (va_status));
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
g_assert (attributes[0].type == VAConfigAttribRTFormat);
|
||||||
|
if (attributes[0].value == VA_ATTRIB_NOT_SUPPORTED ||
|
||||||
|
!(attributes[0].value & VA_RT_FORMAT_YUV420))
|
||||||
|
{
|
||||||
|
g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
|
||||||
|
"Unsuitable device, device does not support YUV420 format");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
g_assert (attributes[1].type == VAConfigAttribRateControl);
|
||||||
|
if (attributes[1].value == VA_ATTRIB_NOT_SUPPORTED ||
|
||||||
|
!(attributes[1].value & VA_RC_CQP))
|
||||||
|
{
|
||||||
|
g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
|
||||||
|
"Unsuitable device, device does not support CQP");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
g_assert (attributes[2].type == VAConfigAttribEncMaxRefFrames);
|
||||||
|
if (attributes[2].value == VA_ATTRIB_NOT_SUPPORTED)
|
||||||
|
{
|
||||||
|
g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
|
||||||
|
"Unsuitable device, invalid max ref frames attribute: %u",
|
||||||
|
attributes[2].value);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
max_ref_l0 = attributes[2].value & 0xFFFF;
|
||||||
|
if (max_ref_l0 < 1)
|
||||||
|
{
|
||||||
|
g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
|
||||||
|
"Unsuitable device, device cannot handle reference frames");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
g_assert (attributes[3].type == VAConfigAttribEncPackedHeaders);
|
||||||
|
if (attributes[3].value == VA_ATTRIB_NOT_SUPPORTED ||
|
||||||
|
!(attributes[3].value & VA_ENC_PACKED_HEADER_SEQUENCE) ||
|
||||||
|
!(attributes[3].value & VA_ENC_PACKED_HEADER_PICTURE) ||
|
||||||
|
!(attributes[3].value & VA_ENC_PACKED_HEADER_SLICE) ||
|
||||||
|
!(attributes[3].value & VA_ENC_PACKED_HEADER_RAW_DATA))
|
||||||
|
{
|
||||||
|
g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
|
||||||
|
"Unsuitable device, device does not support required packed "
|
||||||
|
"headers (only supports 0x%08X)", attributes[3].value);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
g_assert (attributes[4].type == VAConfigAttribEncQualityRange);
|
||||||
|
hwaccel_vaapi->supports_quality_level = attributes[4].value !=
|
||||||
|
VA_ATTRIB_NOT_SUPPORTED;
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
GrdHwAccelVaapi *
|
||||||
|
grd_hwaccel_vaapi_new (GrdVkDevice *vk_device,
|
||||||
|
GError **error)
|
||||||
|
{
|
||||||
|
g_autoptr (GrdHwAccelVaapi) hwaccel_vaapi = NULL;
|
||||||
|
int version_major = 0;
|
||||||
|
int version_minor = 0;
|
||||||
|
|
||||||
|
g_assert (vk_device);
|
||||||
|
|
||||||
|
hwaccel_vaapi = g_object_new (GRD_TYPE_HWACCEL_VAAPI, NULL);
|
||||||
|
hwaccel_vaapi->vk_device = vk_device;
|
||||||
|
|
||||||
|
if (!initialize_vaapi (hwaccel_vaapi, &version_major, &version_minor,
|
||||||
|
grd_vk_device_get_drm_render_node (vk_device),
|
||||||
|
error))
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
if (!check_device_capabilities (hwaccel_vaapi, error))
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
g_message ("[HWAccel.VAAPI] Successfully initialized VAAPI %i.%i with "
|
||||||
|
"vendor: %s", version_major, version_minor,
|
||||||
|
vaQueryVendorString (hwaccel_vaapi->va_display));
|
||||||
|
|
||||||
|
return g_steal_pointer (&hwaccel_vaapi);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
grd_hwaccel_vaapi_dispose (GObject *object)
|
||||||
|
{
|
||||||
|
GrdHwAccelVaapi *hwaccel_vaapi = GRD_HWACCEL_VAAPI (object);
|
||||||
|
|
||||||
|
g_clear_pointer (&hwaccel_vaapi->va_display, vaTerminate);
|
||||||
|
g_clear_fd (&hwaccel_vaapi->drm_fd, NULL);
|
||||||
|
|
||||||
|
G_OBJECT_CLASS (grd_hwaccel_vaapi_parent_class)->dispose (object);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
grd_hwaccel_vaapi_init (GrdHwAccelVaapi *hwaccel_vaapi)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
grd_hwaccel_vaapi_class_init (GrdHwAccelVaapiClass *klass)
|
||||||
|
{
|
||||||
|
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
||||||
|
|
||||||
|
object_class->dispose = grd_hwaccel_vaapi_dispose;
|
||||||
|
}
|
||||||
38
grd-hwaccel-vaapi.h
Normal file
38
grd-hwaccel-vaapi.h
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2022 Pascal Nowack
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License as
|
||||||
|
* published by the Free Software Foundation; either version 2 of the
|
||||||
|
* License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
|
||||||
|
* 02111-1307, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <glib-object.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#include "grd-types.h"
|
||||||
|
|
||||||
|
#define GRD_TYPE_HWACCEL_VAAPI (grd_hwaccel_vaapi_get_type ())
|
||||||
|
G_DECLARE_FINAL_TYPE (GrdHwAccelVaapi, grd_hwaccel_vaapi,
|
||||||
|
GRD, HWACCEL_VAAPI, GObject)
|
||||||
|
|
||||||
|
GrdHwAccelVaapi *grd_hwaccel_vaapi_new (GrdVkDevice *vk_device,
|
||||||
|
GError **error);
|
||||||
|
|
||||||
|
GrdEncodeSession *grd_hwaccel_vaapi_create_encode_session (GrdHwAccelVaapi *hwaccel_vaapi,
|
||||||
|
uint32_t source_width,
|
||||||
|
uint32_t source_height,
|
||||||
|
uint32_t refresh_rate,
|
||||||
|
GError **error);
|
||||||
940
grd-hwaccel-vulkan.c
Normal file
940
grd-hwaccel-vulkan.c
Normal file
@@ -0,0 +1,940 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2022 Pascal Nowack
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License as
|
||||||
|
* published by the Free Software Foundation; either version 2 of the
|
||||||
|
* License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
|
||||||
|
* 02111-1307, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
|
#include "grd-hwaccel-vulkan.h"
|
||||||
|
|
||||||
|
#include <drm_fourcc.h>
|
||||||
|
#include <gio/gio.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <sys/sysmacros.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
|
||||||
|
#include "grd-debug.h"
|
||||||
|
#include "grd-egl-thread.h"
|
||||||
|
#include "grd-vk-device.h"
|
||||||
|
#include "grd-vk-physical-device.h"
|
||||||
|
#include "grd-vk-utils.h"
|
||||||
|
|
||||||
|
#define VULKAN_API_VERSION VK_API_VERSION_1_2
|
||||||
|
|
||||||
|
struct _GrdHwAccelVulkan
|
||||||
|
{
|
||||||
|
GObject parent;
|
||||||
|
|
||||||
|
GrdEglThread *egl_thread;
|
||||||
|
|
||||||
|
VkInstance vk_instance;
|
||||||
|
|
||||||
|
/* VK_EXT_debug_utils */
|
||||||
|
PFN_vkCreateDebugUtilsMessengerEXT vkCreateDebugUtilsMessengerEXT;
|
||||||
|
PFN_vkDestroyDebugUtilsMessengerEXT vkDestroyDebugUtilsMessengerEXT;
|
||||||
|
|
||||||
|
VkDebugUtilsMessengerEXT vk_debug_messenger;
|
||||||
|
|
||||||
|
GrdVkSPIRVSources spirv_sources;
|
||||||
|
};
|
||||||
|
|
||||||
|
G_DEFINE_TYPE (GrdHwAccelVulkan, grd_hwaccel_vulkan, G_TYPE_OBJECT)
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
list_contains_drm_format_modifier (uint64_t *modifiers,
|
||||||
|
int n_modifiers,
|
||||||
|
uint64_t modifier)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < n_modifiers; ++i)
|
||||||
|
{
|
||||||
|
if (modifiers[i] == modifier)
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static GArray *
|
||||||
|
get_egl_vulkan_format_modifier_intersection (GrdHwAccelVulkan *hwaccel_vulkan,
|
||||||
|
VkPhysicalDevice vk_physical_device,
|
||||||
|
uint32_t drm_format,
|
||||||
|
uint32_t *required_n_planes,
|
||||||
|
VkFormatFeatureFlags2 *required_features,
|
||||||
|
GError **error)
|
||||||
|
{
|
||||||
|
GrdEglThread *egl_thread = hwaccel_vulkan->egl_thread;
|
||||||
|
VkFormatProperties2 format_properties_2 = {};
|
||||||
|
VkDrmFormatModifierPropertiesList2EXT modifier_properties_list2 = {};
|
||||||
|
g_autofree VkDrmFormatModifierProperties2EXT *modifier_properties_2 = NULL;
|
||||||
|
VkFormat vk_format = VK_FORMAT_UNDEFINED;
|
||||||
|
g_autofree uint64_t *egl_modifiers = NULL;
|
||||||
|
int n_egl_modifiers;
|
||||||
|
GArray *vk_modifiers;
|
||||||
|
uint32_t i;
|
||||||
|
|
||||||
|
if (!grd_vk_get_vk_format_from_drm_format (drm_format, &vk_format, error))
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
format_properties_2.sType = VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2;
|
||||||
|
modifier_properties_list2.sType =
|
||||||
|
VK_STRUCTURE_TYPE_DRM_FORMAT_MODIFIER_PROPERTIES_LIST_2_EXT;
|
||||||
|
|
||||||
|
grd_vk_append_to_chain (&format_properties_2, &modifier_properties_list2);
|
||||||
|
|
||||||
|
vkGetPhysicalDeviceFormatProperties2 (vk_physical_device, vk_format,
|
||||||
|
&format_properties_2);
|
||||||
|
|
||||||
|
if (modifier_properties_list2.drmFormatModifierCount == 0)
|
||||||
|
{
|
||||||
|
g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
|
||||||
|
"No DRM format modifiers available for DRM format %u",
|
||||||
|
drm_format);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
modifier_properties_2 =
|
||||||
|
g_new0 (VkDrmFormatModifierProperties2EXT,
|
||||||
|
modifier_properties_list2.drmFormatModifierCount);
|
||||||
|
|
||||||
|
modifier_properties_list2.pDrmFormatModifierProperties = modifier_properties_2;
|
||||||
|
vkGetPhysicalDeviceFormatProperties2 (vk_physical_device, vk_format,
|
||||||
|
&format_properties_2);
|
||||||
|
|
||||||
|
if (!grd_egl_thread_get_modifiers_for_format (egl_thread, drm_format,
|
||||||
|
&n_egl_modifiers,
|
||||||
|
&egl_modifiers))
|
||||||
|
{
|
||||||
|
g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
|
||||||
|
"No DRM format modifiers available for DRM format %u from "
|
||||||
|
"EGL", drm_format);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
vk_modifiers = g_array_new (FALSE, FALSE, sizeof (uint64_t));
|
||||||
|
|
||||||
|
for (i = 0; i < modifier_properties_list2.drmFormatModifierCount; i++)
|
||||||
|
{
|
||||||
|
uint64_t modifier = modifier_properties_2[i].drmFormatModifier;
|
||||||
|
uint32_t n_planes = modifier_properties_2[i].drmFormatModifierPlaneCount;
|
||||||
|
VkFormatFeatureFlags2 tiling_features =
|
||||||
|
modifier_properties_2[i].drmFormatModifierTilingFeatures;
|
||||||
|
|
||||||
|
if (required_n_planes &&
|
||||||
|
n_planes != *required_n_planes)
|
||||||
|
continue;
|
||||||
|
if (required_features &&
|
||||||
|
(tiling_features & *required_features) != *required_features)
|
||||||
|
continue;
|
||||||
|
if (!list_contains_drm_format_modifier (egl_modifiers, n_egl_modifiers,
|
||||||
|
modifier))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
g_array_append_vals (vk_modifiers, &modifier, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (vk_modifiers->len == 0)
|
||||||
|
{
|
||||||
|
g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
|
||||||
|
"No common DRM format modifiers available for DRM format %u "
|
||||||
|
"from EGL and Vulkan", drm_format);
|
||||||
|
g_array_free (vk_modifiers, TRUE);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return vk_modifiers;
|
||||||
|
}
|
||||||
|
|
||||||
|
gboolean
|
||||||
|
grd_hwaccel_vulkan_get_modifiers_for_format (GrdHwAccelVulkan *hwaccel_vulkan,
|
||||||
|
GrdVkPhysicalDevice *physical_device,
|
||||||
|
uint32_t drm_format,
|
||||||
|
int *out_n_modifiers,
|
||||||
|
uint64_t **out_modifiers)
|
||||||
|
{
|
||||||
|
VkPhysicalDevice vk_physical_device =
|
||||||
|
grd_vk_physical_device_get_physical_device (physical_device);
|
||||||
|
GArray *modifiers;
|
||||||
|
g_autoptr (GError) error = NULL;
|
||||||
|
|
||||||
|
modifiers =
|
||||||
|
get_egl_vulkan_format_modifier_intersection (hwaccel_vulkan,
|
||||||
|
vk_physical_device,
|
||||||
|
drm_format,
|
||||||
|
NULL, NULL, &error);
|
||||||
|
if (!modifiers)
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
*out_n_modifiers = modifiers->len;
|
||||||
|
*out_modifiers = (uint64_t *) g_array_free (modifiers, FALSE);
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
has_vk_extension (VkExtensionProperties *properties,
|
||||||
|
uint32_t n_properties,
|
||||||
|
const char *extension)
|
||||||
|
{
|
||||||
|
uint32_t i;
|
||||||
|
|
||||||
|
for (i = 0; i < n_properties; ++i)
|
||||||
|
{
|
||||||
|
if (strcmp (properties[i].extensionName, extension) == 0)
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
check_device_extension (VkExtensionProperties *properties,
|
||||||
|
uint32_t n_properties,
|
||||||
|
const char *extension)
|
||||||
|
{
|
||||||
|
if (!has_vk_extension (properties, n_properties, extension))
|
||||||
|
{
|
||||||
|
g_debug ("[HWAccel.Vulkan] Skipping device. Missing extension '%s'",
|
||||||
|
extension);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
check_device_extensions (VkPhysicalDevice vk_physical_device)
|
||||||
|
{
|
||||||
|
g_autofree VkExtensionProperties *properties = NULL;
|
||||||
|
uint32_t n_properties = 0;
|
||||||
|
VkResult vk_result;
|
||||||
|
|
||||||
|
vk_result = vkEnumerateDeviceExtensionProperties (vk_physical_device, NULL,
|
||||||
|
&n_properties, NULL);
|
||||||
|
if (vk_result != VK_SUCCESS)
|
||||||
|
{
|
||||||
|
g_warning ("[HWAccel.Vulkan] Failed to enumerate device extension "
|
||||||
|
"properties: %i", vk_result);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
if (n_properties == 0)
|
||||||
|
return TRUE;
|
||||||
|
|
||||||
|
properties = g_new0 (VkExtensionProperties, n_properties);
|
||||||
|
vk_result = vkEnumerateDeviceExtensionProperties (vk_physical_device, NULL,
|
||||||
|
&n_properties, properties);
|
||||||
|
if (vk_result != VK_SUCCESS)
|
||||||
|
{
|
||||||
|
g_warning ("[HWAccel.Vulkan] Failed to enumerate device extension "
|
||||||
|
"properties: %i", vk_result);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!check_device_extension (properties, n_properties,
|
||||||
|
VK_KHR_SYNCHRONIZATION_2_EXTENSION_NAME))
|
||||||
|
return FALSE;
|
||||||
|
if (!check_device_extension (properties, n_properties,
|
||||||
|
VK_KHR_EXTERNAL_MEMORY_FD_EXTENSION_NAME))
|
||||||
|
return FALSE;
|
||||||
|
if (!check_device_extension (properties, n_properties,
|
||||||
|
VK_EXT_EXTERNAL_MEMORY_DMA_BUF_EXTENSION_NAME))
|
||||||
|
return FALSE;
|
||||||
|
if (!check_device_extension (properties, n_properties,
|
||||||
|
VK_EXT_IMAGE_DRM_FORMAT_MODIFIER_EXTENSION_NAME))
|
||||||
|
return FALSE;
|
||||||
|
if (!check_device_extension (properties, n_properties,
|
||||||
|
VK_KHR_FORMAT_FEATURE_FLAGS_2_EXTENSION_NAME))
|
||||||
|
return FALSE;
|
||||||
|
if (!check_device_extension (properties, n_properties,
|
||||||
|
VK_KHR_ZERO_INITIALIZE_WORKGROUP_MEMORY_EXTENSION_NAME))
|
||||||
|
return FALSE;
|
||||||
|
if (!check_device_extension (properties, n_properties,
|
||||||
|
VK_EXT_PHYSICAL_DEVICE_DRM_EXTENSION_NAME))
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
get_queue_family_properties (VkPhysicalDevice vk_physical_device,
|
||||||
|
VkQueueFamilyProperties2 **properties_2,
|
||||||
|
uint32_t *n_properties_2)
|
||||||
|
{
|
||||||
|
uint32_t i;
|
||||||
|
|
||||||
|
*properties_2 = NULL;
|
||||||
|
*n_properties_2 = 0;
|
||||||
|
|
||||||
|
vkGetPhysicalDeviceQueueFamilyProperties2 (vk_physical_device,
|
||||||
|
n_properties_2, NULL);
|
||||||
|
if (*n_properties_2 == 0)
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
*properties_2 = g_new0 (VkQueueFamilyProperties2, *n_properties_2);
|
||||||
|
for (i = 0; i < *n_properties_2; ++i)
|
||||||
|
(*properties_2)[i].sType = VK_STRUCTURE_TYPE_QUEUE_FAMILY_PROPERTIES_2;
|
||||||
|
|
||||||
|
vkGetPhysicalDeviceQueueFamilyProperties2 (vk_physical_device,
|
||||||
|
n_properties_2, *properties_2);
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
has_queue_family_with_bitmask (VkPhysicalDevice vk_physical_device,
|
||||||
|
VkQueueFlags bitmask)
|
||||||
|
{
|
||||||
|
g_autofree VkQueueFamilyProperties2 *properties_2 = NULL;
|
||||||
|
uint32_t n_properties_2 = 0;
|
||||||
|
uint32_t i;
|
||||||
|
|
||||||
|
if (!get_queue_family_properties (vk_physical_device, &properties_2,
|
||||||
|
&n_properties_2))
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
for (i = 0; i < n_properties_2; ++i)
|
||||||
|
{
|
||||||
|
if (properties_2[i].queueFamilyProperties.queueFlags & bitmask)
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
supports_bgrx_dma_buf_images (GrdHwAccelVulkan *hwaccel_vulkan,
|
||||||
|
VkPhysicalDevice vk_physical_device,
|
||||||
|
GError **error)
|
||||||
|
{
|
||||||
|
uint32_t drm_format;
|
||||||
|
uint32_t required_n_planes;
|
||||||
|
VkFormatFeatureFlags2 required_features;
|
||||||
|
GArray *modifier_array;
|
||||||
|
uint64_t *modifiers;
|
||||||
|
uint32_t i;
|
||||||
|
|
||||||
|
drm_format = DRM_FORMAT_XRGB8888;
|
||||||
|
required_n_planes = 1;
|
||||||
|
required_features = VK_FORMAT_FEATURE_2_SAMPLED_IMAGE_BIT;
|
||||||
|
|
||||||
|
modifier_array =
|
||||||
|
get_egl_vulkan_format_modifier_intersection (hwaccel_vulkan,
|
||||||
|
vk_physical_device,
|
||||||
|
drm_format,
|
||||||
|
&required_n_planes,
|
||||||
|
&required_features,
|
||||||
|
error);
|
||||||
|
if (!modifier_array)
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
modifiers = (uint64_t *) modifier_array->data;
|
||||||
|
for (i = 0; i < modifier_array->len; ++i)
|
||||||
|
{
|
||||||
|
g_debug ("[HWAccel.Vulkan] Found DRM format modifier %lu for DRM "
|
||||||
|
"format %u", modifiers[i], drm_format);
|
||||||
|
}
|
||||||
|
g_array_free (modifier_array, TRUE);
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
check_physical_device (GrdHwAccelVulkan *hwaccel_vulkan,
|
||||||
|
VkPhysicalDevice vk_physical_device,
|
||||||
|
int64_t render_major_egl,
|
||||||
|
int64_t render_minor_egl,
|
||||||
|
GrdVkDeviceFeatures *device_features)
|
||||||
|
{
|
||||||
|
VkPhysicalDeviceProperties properties = {};
|
||||||
|
VkPhysicalDeviceProperties2 properties_2 = {};
|
||||||
|
VkPhysicalDeviceDrmPropertiesEXT drm_properties = {};
|
||||||
|
VkPhysicalDeviceFeatures2 features_2 = {};
|
||||||
|
VkPhysicalDeviceVulkan12Features vulkan12_features = {};
|
||||||
|
VkPhysicalDeviceZeroInitializeWorkgroupMemoryFeatures zero_init_features = {};
|
||||||
|
g_autoptr (GError) error = NULL;
|
||||||
|
|
||||||
|
*device_features = 0;
|
||||||
|
|
||||||
|
vkGetPhysicalDeviceProperties (vk_physical_device, &properties);
|
||||||
|
if (properties.apiVersion < VULKAN_API_VERSION)
|
||||||
|
{
|
||||||
|
g_debug ("[HWAccel.Vulkan] Skipping device. API version too old "
|
||||||
|
"(have %u, need %u)", properties.apiVersion, VULKAN_API_VERSION);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
if (!properties.limits.timestampComputeAndGraphics)
|
||||||
|
{
|
||||||
|
g_debug ("[HWAccel.Vulkan] Skipping device. Support for "
|
||||||
|
"'timestampComputeAndGraphics' is missing");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!check_device_extensions (vk_physical_device))
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
properties_2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2;
|
||||||
|
drm_properties.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DRM_PROPERTIES_EXT;
|
||||||
|
|
||||||
|
grd_vk_append_to_chain (&properties_2, &drm_properties);
|
||||||
|
|
||||||
|
vkGetPhysicalDeviceProperties2 (vk_physical_device, &properties_2);
|
||||||
|
|
||||||
|
if (drm_properties.hasRender != VK_TRUE)
|
||||||
|
{
|
||||||
|
g_debug ("[HWAccel.Vulkan] Skipping device. Device has no "
|
||||||
|
"DRM render node");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
if (drm_properties.renderMajor != render_major_egl ||
|
||||||
|
drm_properties.renderMinor != render_minor_egl)
|
||||||
|
{
|
||||||
|
g_debug ("[HWAccel.Vulkan] Skipping device. Vulkan and EGL device DRM "
|
||||||
|
"render nodes don't match");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
features_2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2;
|
||||||
|
vulkan12_features.sType =
|
||||||
|
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_2_FEATURES;
|
||||||
|
zero_init_features.sType =
|
||||||
|
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ZERO_INITIALIZE_WORKGROUP_MEMORY_FEATURES;
|
||||||
|
|
||||||
|
grd_vk_append_to_chain (&features_2, &vulkan12_features);
|
||||||
|
grd_vk_append_to_chain (&features_2, &zero_init_features);
|
||||||
|
|
||||||
|
vkGetPhysicalDeviceFeatures2 (vk_physical_device, &features_2);
|
||||||
|
|
||||||
|
if (zero_init_features.shaderZeroInitializeWorkgroupMemory != VK_TRUE)
|
||||||
|
{
|
||||||
|
g_debug ("[HWAccel.Vulkan] Skipping device. Support for "
|
||||||
|
"'shaderZeroInitializeWorkgroupMemory' is missing");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (vulkan12_features.descriptorBindingSampledImageUpdateAfterBind != VK_FALSE)
|
||||||
|
*device_features |= GRD_VK_DEVICE_FEATURE_UPDATE_AFTER_BIND_SAMPLED_IMAGE;
|
||||||
|
if (vulkan12_features.descriptorBindingStorageImageUpdateAfterBind != VK_FALSE)
|
||||||
|
*device_features |= GRD_VK_DEVICE_FEATURE_UPDATE_AFTER_BIND_STORAGE_IMAGE;
|
||||||
|
|
||||||
|
if (!has_queue_family_with_bitmask (vk_physical_device,
|
||||||
|
VK_QUEUE_COMPUTE_BIT |
|
||||||
|
VK_QUEUE_TRANSFER_BIT))
|
||||||
|
{
|
||||||
|
g_debug ("[HWAccel.Vulkan] Skipping device. Missing device queue family "
|
||||||
|
"with (VK_QUEUE_COMPUTE_BIT | VK_QUEUE_TRANSFER_BIT) bitmask");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!supports_bgrx_dma_buf_images (hwaccel_vulkan, vk_physical_device,
|
||||||
|
&error))
|
||||||
|
{
|
||||||
|
g_debug ("[HWAccel.Vulkan] Skipping device: %s", error->message);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static GrdVkPhysicalDevice *
|
||||||
|
find_and_create_physical_device (GrdHwAccelVulkan *hwaccel_vulkan,
|
||||||
|
const VkPhysicalDevice *physical_devices,
|
||||||
|
uint32_t n_physical_devices,
|
||||||
|
int64_t render_major,
|
||||||
|
int64_t render_minor,
|
||||||
|
GError **error)
|
||||||
|
{
|
||||||
|
uint32_t i;
|
||||||
|
|
||||||
|
for (i = 0; i < n_physical_devices; ++i)
|
||||||
|
{
|
||||||
|
VkPhysicalDevice vk_physical_device = physical_devices[i];
|
||||||
|
GrdVkDeviceFeatures device_features = 0;
|
||||||
|
|
||||||
|
g_debug ("[HWAccel.Vulkan] Checking physical device %u/%u",
|
||||||
|
i + 1, n_physical_devices);
|
||||||
|
if (check_physical_device (hwaccel_vulkan, vk_physical_device,
|
||||||
|
render_major, render_minor, &device_features))
|
||||||
|
return grd_vk_physical_device_new (vk_physical_device, device_features);
|
||||||
|
}
|
||||||
|
|
||||||
|
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
|
||||||
|
"Could not find proper device");
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
GrdVkPhysicalDevice *
|
||||||
|
grd_hwaccel_vulkan_acquire_physical_device (GrdHwAccelVulkan *hwaccel_vulkan,
|
||||||
|
GError **error)
|
||||||
|
{
|
||||||
|
GrdEglThread *egl_thread = hwaccel_vulkan->egl_thread;
|
||||||
|
g_autofree VkPhysicalDevice *physical_devices = NULL;
|
||||||
|
uint32_t n_physical_devices = 0;
|
||||||
|
const char *drm_render_node;
|
||||||
|
struct stat stat_buf = {};
|
||||||
|
VkResult vk_result;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
g_assert (egl_thread);
|
||||||
|
|
||||||
|
vk_result = vkEnumeratePhysicalDevices (hwaccel_vulkan->vk_instance,
|
||||||
|
&n_physical_devices, NULL);
|
||||||
|
if (vk_result != VK_SUCCESS)
|
||||||
|
{
|
||||||
|
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
|
||||||
|
"Failed to enumerate physical devices: %i", vk_result);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
if (n_physical_devices == 0)
|
||||||
|
{
|
||||||
|
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
|
||||||
|
"No physical devices found: %i", vk_result);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
physical_devices = g_new0 (VkPhysicalDevice, n_physical_devices);
|
||||||
|
vk_result = vkEnumeratePhysicalDevices (hwaccel_vulkan->vk_instance,
|
||||||
|
&n_physical_devices,
|
||||||
|
physical_devices);
|
||||||
|
if (vk_result != VK_SUCCESS)
|
||||||
|
{
|
||||||
|
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
|
||||||
|
"Failed to enumerate physical devices: %i", vk_result);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
drm_render_node = grd_egl_thread_get_drm_render_node (egl_thread);
|
||||||
|
|
||||||
|
ret = stat (drm_render_node, &stat_buf);
|
||||||
|
if (ret < 0)
|
||||||
|
{
|
||||||
|
g_set_error (error, G_IO_ERROR, g_io_error_from_errno (-ret),
|
||||||
|
"Failed to check status of DRM render node: %s",
|
||||||
|
strerror (-ret));
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
if (!S_ISCHR (stat_buf.st_mode))
|
||||||
|
{
|
||||||
|
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
|
||||||
|
"Invalid file mode for DRM render node");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return find_and_create_physical_device (hwaccel_vulkan,
|
||||||
|
physical_devices, n_physical_devices,
|
||||||
|
major (stat_buf.st_rdev),
|
||||||
|
minor (stat_buf.st_rdev),
|
||||||
|
error);
|
||||||
|
}
|
||||||
|
|
||||||
|
GrdVkDevice *
|
||||||
|
grd_hwaccel_vulkan_acquire_device (GrdHwAccelVulkan *hwaccel_vulkan,
|
||||||
|
GrdVkPhysicalDevice *physical_device,
|
||||||
|
GError **error)
|
||||||
|
{
|
||||||
|
return grd_vk_device_new (physical_device,
|
||||||
|
&hwaccel_vulkan->spirv_sources,
|
||||||
|
error);
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
has_vk_layer (VkLayerProperties *properties,
|
||||||
|
uint32_t n_properties,
|
||||||
|
const char *extension)
|
||||||
|
{
|
||||||
|
uint32_t i;
|
||||||
|
|
||||||
|
for (i = 0; i < n_properties; ++i)
|
||||||
|
{
|
||||||
|
if (strcmp (properties[i].layerName, extension) == 0)
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
check_instance_layers (GrdHwAccelVulkan *hwaccel_vulkan,
|
||||||
|
gboolean *has_validation_layer,
|
||||||
|
GError **error)
|
||||||
|
{
|
||||||
|
g_autofree VkLayerProperties *properties = NULL;
|
||||||
|
uint32_t n_properties = 0;
|
||||||
|
VkResult vk_result;
|
||||||
|
|
||||||
|
*has_validation_layer = FALSE;
|
||||||
|
|
||||||
|
vk_result = vkEnumerateInstanceLayerProperties (&n_properties, NULL);
|
||||||
|
if (vk_result != VK_SUCCESS)
|
||||||
|
{
|
||||||
|
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
|
||||||
|
"Failed to enumerate instance layer properties: %i",
|
||||||
|
vk_result);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
if (n_properties == 0)
|
||||||
|
return TRUE;
|
||||||
|
|
||||||
|
properties = g_new0 (VkLayerProperties, n_properties);
|
||||||
|
vk_result = vkEnumerateInstanceLayerProperties (&n_properties, properties);
|
||||||
|
if (vk_result == VK_INCOMPLETE)
|
||||||
|
{
|
||||||
|
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
|
||||||
|
"Instance layer properties changed during count fetch");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
if (vk_result != VK_SUCCESS)
|
||||||
|
{
|
||||||
|
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
|
||||||
|
"Failed to enumerate instance layer properties: %i",
|
||||||
|
vk_result);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
*has_validation_layer = has_vk_layer (properties, n_properties,
|
||||||
|
"VK_LAYER_KHRONOS_validation");
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
check_instance_extensions (GrdHwAccelVulkan *hwaccel_vulkan,
|
||||||
|
gboolean *supports_debug_utils,
|
||||||
|
GError **error)
|
||||||
|
{
|
||||||
|
g_autofree VkExtensionProperties *properties = NULL;
|
||||||
|
uint32_t n_properties = 0;
|
||||||
|
VkResult vk_result;
|
||||||
|
|
||||||
|
*supports_debug_utils = FALSE;
|
||||||
|
|
||||||
|
vk_result = vkEnumerateInstanceExtensionProperties (NULL,
|
||||||
|
&n_properties,
|
||||||
|
NULL);
|
||||||
|
if (vk_result != VK_SUCCESS)
|
||||||
|
{
|
||||||
|
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
|
||||||
|
"Failed to enumerate instance extension properties: %i",
|
||||||
|
vk_result);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
if (n_properties == 0)
|
||||||
|
return TRUE;
|
||||||
|
|
||||||
|
properties = g_new0 (VkExtensionProperties, n_properties);
|
||||||
|
vk_result = vkEnumerateInstanceExtensionProperties (NULL,
|
||||||
|
&n_properties,
|
||||||
|
properties);
|
||||||
|
if (vk_result == VK_INCOMPLETE)
|
||||||
|
{
|
||||||
|
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
|
||||||
|
"Instance extension properties changed during count fetch");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
if (vk_result != VK_SUCCESS)
|
||||||
|
{
|
||||||
|
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
|
||||||
|
"Failed to enumerate instance extension properties: %i",
|
||||||
|
vk_result);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
*supports_debug_utils = has_vk_extension (properties, n_properties,
|
||||||
|
VK_EXT_DEBUG_UTILS_EXTENSION_NAME);
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
load_instance_debug_funcs (GrdHwAccelVulkan *hwaccel_vulkan,
|
||||||
|
GError **error)
|
||||||
|
{
|
||||||
|
VkInstance vk_instance = hwaccel_vulkan->vk_instance;
|
||||||
|
|
||||||
|
hwaccel_vulkan->vkCreateDebugUtilsMessengerEXT = (PFN_vkCreateDebugUtilsMessengerEXT)
|
||||||
|
vkGetInstanceProcAddr (vk_instance, "vkCreateDebugUtilsMessengerEXT");
|
||||||
|
if (!hwaccel_vulkan->vkCreateDebugUtilsMessengerEXT)
|
||||||
|
{
|
||||||
|
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
|
||||||
|
"Failed to get instance function address for function "
|
||||||
|
"\"vkCreateDebugUtilsMessengerEXT\"");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
hwaccel_vulkan->vkDestroyDebugUtilsMessengerEXT = (PFN_vkDestroyDebugUtilsMessengerEXT)
|
||||||
|
vkGetInstanceProcAddr (vk_instance, "vkDestroyDebugUtilsMessengerEXT");
|
||||||
|
if (!hwaccel_vulkan->vkDestroyDebugUtilsMessengerEXT)
|
||||||
|
{
|
||||||
|
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
|
||||||
|
"Failed to get instance function address for function "
|
||||||
|
"\"vkDestroyDebugUtilsMessengerEXT\"");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const char *
|
||||||
|
message_severity_to_string (VkDebugUtilsMessageSeverityFlagBitsEXT message_severity)
|
||||||
|
{
|
||||||
|
switch (message_severity)
|
||||||
|
{
|
||||||
|
case VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT:
|
||||||
|
return "Verbose";
|
||||||
|
case VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT:
|
||||||
|
return "Info";
|
||||||
|
case VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT:
|
||||||
|
return "Warning";
|
||||||
|
case VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT:
|
||||||
|
return "Error";
|
||||||
|
default:
|
||||||
|
g_assert_not_reached ();
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static VkBool32
|
||||||
|
debug_messenger (VkDebugUtilsMessageSeverityFlagBitsEXT message_severity,
|
||||||
|
VkDebugUtilsMessageTypeFlagsEXT message_type_flags,
|
||||||
|
const VkDebugUtilsMessengerCallbackDataEXT *callback_data,
|
||||||
|
void* user_data)
|
||||||
|
{
|
||||||
|
g_debug ("[HWAccel.Vulkan] Debug[%s]: Types: 0x%08X: Id: [%i: %s]: %s",
|
||||||
|
message_severity_to_string (message_severity), message_type_flags,
|
||||||
|
callback_data->messageIdNumber, callback_data->pMessageIdName,
|
||||||
|
callback_data->pMessage);
|
||||||
|
|
||||||
|
return VK_FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
create_debug_messenger (GrdHwAccelVulkan *hwaccel_vulkan,
|
||||||
|
GError **error)
|
||||||
|
{
|
||||||
|
VkInstance vk_instance = hwaccel_vulkan->vk_instance;
|
||||||
|
VkDebugUtilsMessengerCreateInfoEXT create_info = {};
|
||||||
|
VkDebugUtilsMessengerEXT vk_debug_messenger = VK_NULL_HANDLE;
|
||||||
|
VkResult vk_result;
|
||||||
|
|
||||||
|
g_assert (vk_instance != VK_NULL_HANDLE);
|
||||||
|
g_assert (hwaccel_vulkan->vkCreateDebugUtilsMessengerEXT);
|
||||||
|
|
||||||
|
create_info.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT;
|
||||||
|
create_info.messageSeverity = VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT |
|
||||||
|
VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT |
|
||||||
|
VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT |
|
||||||
|
VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT;
|
||||||
|
create_info.messageType = VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT |
|
||||||
|
VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT |
|
||||||
|
VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT;
|
||||||
|
create_info.pfnUserCallback = debug_messenger;
|
||||||
|
create_info.pUserData = NULL;
|
||||||
|
|
||||||
|
vk_result =
|
||||||
|
hwaccel_vulkan->vkCreateDebugUtilsMessengerEXT (vk_instance,
|
||||||
|
&create_info, NULL,
|
||||||
|
&vk_debug_messenger);
|
||||||
|
if (vk_result != VK_SUCCESS)
|
||||||
|
{
|
||||||
|
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
|
||||||
|
"Failed to create debug messenger: %i", vk_result);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
g_assert (vk_debug_messenger != VK_NULL_HANDLE);
|
||||||
|
|
||||||
|
hwaccel_vulkan->vk_debug_messenger = vk_debug_messenger;
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
create_vk_instance (GrdHwAccelVulkan *hwaccel_vulkan,
|
||||||
|
GError **error)
|
||||||
|
{
|
||||||
|
VkInstanceCreateInfo instance_create_info = {};
|
||||||
|
VkApplicationInfo app_info = {};
|
||||||
|
gboolean has_validation_layer = FALSE;
|
||||||
|
gboolean supports_debug_utils = FALSE;
|
||||||
|
gboolean vulkan_debug = FALSE;
|
||||||
|
const char *layers[1] = {};
|
||||||
|
uint32_t n_layers = 0;
|
||||||
|
const char *extensions[1] = {};
|
||||||
|
uint32_t n_extensions = 0;
|
||||||
|
VkResult vk_result;
|
||||||
|
|
||||||
|
if (grd_get_debug_flags () & GRD_DEBUG_VK_VALIDATION)
|
||||||
|
vulkan_debug = TRUE;
|
||||||
|
|
||||||
|
if (!check_instance_layers (hwaccel_vulkan, &has_validation_layer, error))
|
||||||
|
return FALSE;
|
||||||
|
if (!check_instance_extensions (hwaccel_vulkan, &supports_debug_utils, error))
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
app_info.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO;
|
||||||
|
app_info.apiVersion = VULKAN_API_VERSION;
|
||||||
|
|
||||||
|
if (vulkan_debug)
|
||||||
|
{
|
||||||
|
if (has_validation_layer)
|
||||||
|
layers[n_layers++] = "VK_LAYER_KHRONOS_validation";
|
||||||
|
else
|
||||||
|
g_warning ("[HWAccel.Vulkan] VK_LAYER_KHRONOS_validation is unavailable");
|
||||||
|
|
||||||
|
if (supports_debug_utils)
|
||||||
|
extensions[n_extensions++] = VK_EXT_DEBUG_UTILS_EXTENSION_NAME;
|
||||||
|
else
|
||||||
|
g_warning ("[HWAccel.Vulkan] VK_EXT_debug_utils is unavailable");
|
||||||
|
}
|
||||||
|
|
||||||
|
instance_create_info.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
|
||||||
|
instance_create_info.pApplicationInfo = &app_info;
|
||||||
|
instance_create_info.enabledLayerCount = n_layers;
|
||||||
|
instance_create_info.ppEnabledLayerNames = layers;
|
||||||
|
instance_create_info.enabledExtensionCount = n_extensions;
|
||||||
|
instance_create_info.ppEnabledExtensionNames = extensions;
|
||||||
|
|
||||||
|
vk_result = vkCreateInstance (&instance_create_info, NULL,
|
||||||
|
&hwaccel_vulkan->vk_instance);
|
||||||
|
if (vk_result != VK_SUCCESS)
|
||||||
|
{
|
||||||
|
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
|
||||||
|
"Failed to create instance: %i", vk_result);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
g_assert (hwaccel_vulkan->vk_instance != VK_NULL_HANDLE);
|
||||||
|
|
||||||
|
if (!vulkan_debug || !supports_debug_utils)
|
||||||
|
return TRUE;
|
||||||
|
|
||||||
|
if (!load_instance_debug_funcs (hwaccel_vulkan, error))
|
||||||
|
return FALSE;
|
||||||
|
if (!create_debug_messenger (hwaccel_vulkan, error))
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
GrdHwAccelVulkan *
|
||||||
|
grd_hwaccel_vulkan_new (GrdEglThread *egl_thread,
|
||||||
|
GError **error)
|
||||||
|
{
|
||||||
|
g_autoptr (GrdHwAccelVulkan) hwaccel_vulkan = NULL;
|
||||||
|
|
||||||
|
hwaccel_vulkan = g_object_new (GRD_TYPE_HWACCEL_VULKAN, NULL);
|
||||||
|
hwaccel_vulkan->egl_thread = egl_thread;
|
||||||
|
|
||||||
|
if (!create_vk_instance (hwaccel_vulkan, error))
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
return g_steal_pointer (&hwaccel_vulkan);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
spirv_source_free (GrdVkSPIRVSource *spirv_source)
|
||||||
|
{
|
||||||
|
g_free (spirv_source->data);
|
||||||
|
g_free (spirv_source);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
free_spirv_sources (GrdHwAccelVulkan *hwaccel_vulkan)
|
||||||
|
{
|
||||||
|
GrdVkSPIRVSources *spirv_sources = &hwaccel_vulkan->spirv_sources;
|
||||||
|
|
||||||
|
g_clear_pointer (&spirv_sources->avc_dual_view, spirv_source_free);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
grd_hwaccel_vulkan_dispose (GObject *object)
|
||||||
|
{
|
||||||
|
GrdHwAccelVulkan *hwaccel_vulkan = GRD_HWACCEL_VULKAN (object);
|
||||||
|
|
||||||
|
free_spirv_sources (hwaccel_vulkan);
|
||||||
|
|
||||||
|
if (hwaccel_vulkan->vk_debug_messenger != VK_NULL_HANDLE)
|
||||||
|
{
|
||||||
|
VkInstance vk_instance = hwaccel_vulkan->vk_instance;
|
||||||
|
VkDebugUtilsMessengerEXT vk_debug_messenger =
|
||||||
|
hwaccel_vulkan->vk_debug_messenger;
|
||||||
|
|
||||||
|
g_assert (vk_instance != VK_NULL_HANDLE);
|
||||||
|
g_assert (hwaccel_vulkan->vkDestroyDebugUtilsMessengerEXT);
|
||||||
|
|
||||||
|
hwaccel_vulkan->vkDestroyDebugUtilsMessengerEXT (vk_instance,
|
||||||
|
vk_debug_messenger,
|
||||||
|
NULL);
|
||||||
|
hwaccel_vulkan->vk_debug_messenger = VK_NULL_HANDLE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hwaccel_vulkan->vk_instance != VK_NULL_HANDLE)
|
||||||
|
{
|
||||||
|
vkDestroyInstance (hwaccel_vulkan->vk_instance, NULL);
|
||||||
|
hwaccel_vulkan->vk_instance = VK_NULL_HANDLE;
|
||||||
|
}
|
||||||
|
|
||||||
|
G_OBJECT_CLASS (grd_hwaccel_vulkan_parent_class)->dispose (object);
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
load_spirv_source (const char *path,
|
||||||
|
GrdVkSPIRVSource **spirv_source,
|
||||||
|
GError **error)
|
||||||
|
{
|
||||||
|
char *data = NULL;
|
||||||
|
size_t size = 0;
|
||||||
|
|
||||||
|
if (!g_file_get_contents (path, &data, &size, error))
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
/* SPIR-V sources are always aligned to 32 bits */
|
||||||
|
g_assert (size % 4 == 0);
|
||||||
|
|
||||||
|
*spirv_source = g_new0 (GrdVkSPIRVSource, 1);
|
||||||
|
(*spirv_source)->data = data;
|
||||||
|
(*spirv_source)->size = size;
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
load_spirv_sources (GrdHwAccelVulkan *hwaccel_vulkan)
|
||||||
|
{
|
||||||
|
GrdVkSPIRVSources *spirv_sources = &hwaccel_vulkan->spirv_sources;
|
||||||
|
g_autofree char *avc_dual_view_path = NULL;
|
||||||
|
g_autoptr (GError) error = NULL;
|
||||||
|
|
||||||
|
avc_dual_view_path = g_strdup_printf ("%s/grd-avc-dual-view_opt.spv",
|
||||||
|
GRD_SHADER_DIR);
|
||||||
|
if (!load_spirv_source (avc_dual_view_path, &spirv_sources->avc_dual_view,
|
||||||
|
&error))
|
||||||
|
g_error ("[HWAccel.Vulkan] Failed to load shader: %s", error->message);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
grd_hwaccel_vulkan_init (GrdHwAccelVulkan *hwaccel_vulkan)
|
||||||
|
{
|
||||||
|
load_spirv_sources (hwaccel_vulkan);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
grd_hwaccel_vulkan_class_init (GrdHwAccelVulkanClass *klass)
|
||||||
|
{
|
||||||
|
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
||||||
|
|
||||||
|
object_class->dispose = grd_hwaccel_vulkan_dispose;
|
||||||
|
}
|
||||||
56
grd-hwaccel-vulkan.h
Normal file
56
grd-hwaccel-vulkan.h
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2022 Pascal Nowack
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License as
|
||||||
|
* published by the Free Software Foundation; either version 2 of the
|
||||||
|
* License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
|
||||||
|
* 02111-1307, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <glib-object.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#include "grd-types.h"
|
||||||
|
|
||||||
|
#define GRD_TYPE_HWACCEL_VULKAN (grd_hwaccel_vulkan_get_type ())
|
||||||
|
G_DECLARE_FINAL_TYPE (GrdHwAccelVulkan, grd_hwaccel_vulkan,
|
||||||
|
GRD, HWACCEL_VULKAN, GObject)
|
||||||
|
|
||||||
|
struct _GrdVkSPIRVSource
|
||||||
|
{
|
||||||
|
char *data;
|
||||||
|
size_t size;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct _GrdVkSPIRVSources
|
||||||
|
{
|
||||||
|
GrdVkSPIRVSource *avc_dual_view;
|
||||||
|
};
|
||||||
|
|
||||||
|
GrdHwAccelVulkan *grd_hwaccel_vulkan_new (GrdEglThread *egl_thread,
|
||||||
|
GError **error);
|
||||||
|
|
||||||
|
gboolean grd_hwaccel_vulkan_get_modifiers_for_format (GrdHwAccelVulkan *hwaccel_vulkan,
|
||||||
|
GrdVkPhysicalDevice *physical_device,
|
||||||
|
uint32_t drm_format,
|
||||||
|
int *out_n_modifiers,
|
||||||
|
uint64_t **out_modifiers);
|
||||||
|
|
||||||
|
GrdVkPhysicalDevice *grd_hwaccel_vulkan_acquire_physical_device (GrdHwAccelVulkan *hwaccel_vulkan,
|
||||||
|
GError **error);
|
||||||
|
|
||||||
|
GrdVkDevice *grd_hwaccel_vulkan_acquire_device (GrdHwAccelVulkan *hwaccel_vulkan,
|
||||||
|
GrdVkPhysicalDevice *physical_device,
|
||||||
|
GError **error);
|
||||||
123
grd-image-view-nv12.c
Normal file
123
grd-image-view-nv12.c
Normal file
@@ -0,0 +1,123 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2024 Pascal Nowack
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License as
|
||||||
|
* published by the Free Software Foundation; either version 2 of the
|
||||||
|
* License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
|
||||||
|
* 02111-1307, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
|
#include "grd-image-view-nv12.h"
|
||||||
|
|
||||||
|
#include "grd-vk-image.h"
|
||||||
|
|
||||||
|
struct _GrdImageViewNV12
|
||||||
|
{
|
||||||
|
GrdImageView parent;
|
||||||
|
|
||||||
|
GrdVkImage *vk_y_layer;
|
||||||
|
GrdVkImage *vk_uv_layer;
|
||||||
|
};
|
||||||
|
|
||||||
|
G_DEFINE_TYPE (GrdImageViewNV12, grd_image_view_nv12,
|
||||||
|
GRD_TYPE_IMAGE_VIEW)
|
||||||
|
|
||||||
|
VkImageView
|
||||||
|
grd_image_view_nv12_get_y_layer (GrdImageViewNV12 *image_view_nv12)
|
||||||
|
{
|
||||||
|
return grd_vk_image_get_image_view (image_view_nv12->vk_y_layer);
|
||||||
|
}
|
||||||
|
|
||||||
|
VkImageView
|
||||||
|
grd_image_view_nv12_get_uv_layer (GrdImageViewNV12 *image_view_nv12)
|
||||||
|
{
|
||||||
|
return grd_vk_image_get_image_view (image_view_nv12->vk_uv_layer);
|
||||||
|
}
|
||||||
|
|
||||||
|
GList *
|
||||||
|
grd_image_view_nv12_get_images (GrdImageViewNV12 *image_view_nv12)
|
||||||
|
{
|
||||||
|
GList *images = NULL;
|
||||||
|
|
||||||
|
if (image_view_nv12->vk_y_layer)
|
||||||
|
images = g_list_append (images, image_view_nv12->vk_y_layer);
|
||||||
|
if (image_view_nv12->vk_uv_layer)
|
||||||
|
images = g_list_append (images, image_view_nv12->vk_uv_layer);
|
||||||
|
|
||||||
|
return images;
|
||||||
|
}
|
||||||
|
|
||||||
|
VkImageLayout
|
||||||
|
grd_image_view_nv12_get_image_layout (GrdImageViewNV12 *image_view_nv12)
|
||||||
|
{
|
||||||
|
g_assert (grd_vk_image_get_image_layout (image_view_nv12->vk_y_layer) ==
|
||||||
|
grd_vk_image_get_image_layout (image_view_nv12->vk_uv_layer));
|
||||||
|
|
||||||
|
return grd_vk_image_get_image_layout (image_view_nv12->vk_y_layer);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
grd_image_view_nv12_set_image_layout (GrdImageViewNV12 *image_view_nv12,
|
||||||
|
VkImageLayout vk_image_layout)
|
||||||
|
{
|
||||||
|
grd_vk_image_set_image_layout (image_view_nv12->vk_y_layer, vk_image_layout);
|
||||||
|
grd_vk_image_set_image_layout (image_view_nv12->vk_uv_layer, vk_image_layout);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
grd_image_view_nv12_notify_image_view_release (GrdImageView *image_view)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
GrdImageViewNV12 *
|
||||||
|
grd_image_view_nv12_new (GrdVkImage *vk_y_layer,
|
||||||
|
GrdVkImage *vk_uv_layer)
|
||||||
|
{
|
||||||
|
GrdImageViewNV12 *image_view_nv12;
|
||||||
|
|
||||||
|
image_view_nv12 = g_object_new (GRD_TYPE_IMAGE_VIEW_NV12, NULL);
|
||||||
|
image_view_nv12->vk_y_layer = vk_y_layer;
|
||||||
|
image_view_nv12->vk_uv_layer = vk_uv_layer;
|
||||||
|
|
||||||
|
return image_view_nv12;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
grd_image_view_nv12_dispose (GObject *object)
|
||||||
|
{
|
||||||
|
GrdImageViewNV12 *image_view_nv12 = GRD_IMAGE_VIEW_NV12 (object);
|
||||||
|
|
||||||
|
g_clear_object (&image_view_nv12->vk_uv_layer);
|
||||||
|
g_clear_object (&image_view_nv12->vk_y_layer);
|
||||||
|
|
||||||
|
G_OBJECT_CLASS (grd_image_view_nv12_parent_class)->dispose (object);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
grd_image_view_nv12_init (GrdImageViewNV12 *image_view_nv12)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
grd_image_view_nv12_class_init (GrdImageViewNV12Class *klass)
|
||||||
|
{
|
||||||
|
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
||||||
|
GrdImageViewClass *image_view_class = GRD_IMAGE_VIEW_CLASS (klass);
|
||||||
|
|
||||||
|
object_class->dispose = grd_image_view_nv12_dispose;
|
||||||
|
|
||||||
|
image_view_class->notify_image_view_release =
|
||||||
|
grd_image_view_nv12_notify_image_view_release;
|
||||||
|
}
|
||||||
43
grd-image-view-nv12.h
Normal file
43
grd-image-view-nv12.h
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2024 Pascal Nowack
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License as
|
||||||
|
* published by the Free Software Foundation; either version 2 of the
|
||||||
|
* License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
|
||||||
|
* 02111-1307, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <vulkan/vulkan.h>
|
||||||
|
|
||||||
|
#include "grd-image-view.h"
|
||||||
|
#include "grd-types.h"
|
||||||
|
|
||||||
|
#define GRD_TYPE_IMAGE_VIEW_NV12 (grd_image_view_nv12_get_type ())
|
||||||
|
G_DECLARE_FINAL_TYPE (GrdImageViewNV12, grd_image_view_nv12,
|
||||||
|
GRD, IMAGE_VIEW_NV12, GrdImageView)
|
||||||
|
|
||||||
|
GrdImageViewNV12 *grd_image_view_nv12_new (GrdVkImage *vk_y_layer,
|
||||||
|
GrdVkImage *vk_uv_layer);
|
||||||
|
|
||||||
|
VkImageView grd_image_view_nv12_get_y_layer (GrdImageViewNV12 *image_view_nv12);
|
||||||
|
|
||||||
|
VkImageView grd_image_view_nv12_get_uv_layer (GrdImageViewNV12 *image_view_nv12);
|
||||||
|
|
||||||
|
GList *grd_image_view_nv12_get_images (GrdImageViewNV12 *image_view_nv12);
|
||||||
|
|
||||||
|
VkImageLayout grd_image_view_nv12_get_image_layout (GrdImageViewNV12 *image_view_nv12);
|
||||||
|
|
||||||
|
void grd_image_view_nv12_set_image_layout (GrdImageViewNV12 *image_view_nv12,
|
||||||
|
VkImageLayout vk_image_layout);
|
||||||
89
grd-image-view-rgb.c
Normal file
89
grd-image-view-rgb.c
Normal file
@@ -0,0 +1,89 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2024 Pascal Nowack
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License as
|
||||||
|
* published by the Free Software Foundation; either version 2 of the
|
||||||
|
* License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
|
||||||
|
* 02111-1307, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
|
#include "grd-image-view-rgb.h"
|
||||||
|
|
||||||
|
struct _GrdImageViewRGB
|
||||||
|
{
|
||||||
|
GrdImageView parent;
|
||||||
|
|
||||||
|
GrdLocalBuffer *local_buffer;
|
||||||
|
GrdImageViewRGBReleaseCallback on_release;
|
||||||
|
gpointer on_release_user_data;
|
||||||
|
};
|
||||||
|
|
||||||
|
G_DEFINE_TYPE (GrdImageViewRGB, grd_image_view_rgb,
|
||||||
|
GRD_TYPE_IMAGE_VIEW)
|
||||||
|
|
||||||
|
GrdLocalBuffer *
|
||||||
|
grd_image_view_rgb_get_local_buffer (GrdImageViewRGB *image_view_rgb)
|
||||||
|
{
|
||||||
|
return image_view_rgb->local_buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
grd_image_view_rgb_attach_local_buffer (GrdImageViewRGB *image_view_rgb,
|
||||||
|
GrdLocalBuffer *local_buffer,
|
||||||
|
GrdImageViewRGBReleaseCallback callback,
|
||||||
|
gpointer user_data)
|
||||||
|
{
|
||||||
|
g_assert (!image_view_rgb->local_buffer);
|
||||||
|
|
||||||
|
image_view_rgb->local_buffer = local_buffer;
|
||||||
|
image_view_rgb->on_release = callback;
|
||||||
|
image_view_rgb->on_release_user_data = user_data;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
grd_image_view_rgb_notify_image_view_release (GrdImageView *image_view)
|
||||||
|
{
|
||||||
|
GrdImageViewRGB *image_view_rgb = GRD_IMAGE_VIEW_RGB (image_view);
|
||||||
|
|
||||||
|
if (!image_view_rgb->on_release)
|
||||||
|
return;
|
||||||
|
|
||||||
|
image_view_rgb->on_release (image_view_rgb->on_release_user_data,
|
||||||
|
image_view_rgb->local_buffer);
|
||||||
|
|
||||||
|
image_view_rgb->local_buffer = NULL;
|
||||||
|
image_view_rgb->on_release = NULL;
|
||||||
|
image_view_rgb->on_release_user_data = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
GrdImageViewRGB *
|
||||||
|
grd_image_view_rgb_new (void)
|
||||||
|
{
|
||||||
|
return g_object_new (GRD_TYPE_IMAGE_VIEW_RGB, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
grd_image_view_rgb_init (GrdImageViewRGB *image_view_rgb)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
grd_image_view_rgb_class_init (GrdImageViewRGBClass *klass)
|
||||||
|
{
|
||||||
|
GrdImageViewClass *image_view_class = GRD_IMAGE_VIEW_CLASS (klass);
|
||||||
|
|
||||||
|
image_view_class->notify_image_view_release =
|
||||||
|
grd_image_view_rgb_notify_image_view_release;
|
||||||
|
}
|
||||||
39
grd-image-view-rgb.h
Normal file
39
grd-image-view-rgb.h
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2024 Pascal Nowack
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License as
|
||||||
|
* published by the Free Software Foundation; either version 2 of the
|
||||||
|
* License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
|
||||||
|
* 02111-1307, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "grd-image-view.h"
|
||||||
|
#include "grd-types.h"
|
||||||
|
|
||||||
|
#define GRD_TYPE_IMAGE_VIEW_RGB (grd_image_view_rgb_get_type ())
|
||||||
|
G_DECLARE_FINAL_TYPE (GrdImageViewRGB, grd_image_view_rgb,
|
||||||
|
GRD, IMAGE_VIEW_RGB, GrdImageView)
|
||||||
|
|
||||||
|
typedef void (* GrdImageViewRGBReleaseCallback) (gpointer user_data,
|
||||||
|
GrdLocalBuffer *local_buffer);
|
||||||
|
|
||||||
|
GrdImageViewRGB *grd_image_view_rgb_new (void);
|
||||||
|
|
||||||
|
GrdLocalBuffer *grd_image_view_rgb_get_local_buffer (GrdImageViewRGB *image_view_rgb);
|
||||||
|
|
||||||
|
void grd_image_view_rgb_attach_local_buffer (GrdImageViewRGB *image_view_rgb,
|
||||||
|
GrdLocalBuffer *local_buffer,
|
||||||
|
GrdImageViewRGBReleaseCallback callback,
|
||||||
|
gpointer user_data);
|
||||||
43
grd-image-view.c
Normal file
43
grd-image-view.c
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2024 Pascal Nowack
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License as
|
||||||
|
* published by the Free Software Foundation; either version 2 of the
|
||||||
|
* License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
|
||||||
|
* 02111-1307, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
|
#include "grd-image-view.h"
|
||||||
|
|
||||||
|
G_DEFINE_ABSTRACT_TYPE (GrdImageView, grd_image_view,
|
||||||
|
G_TYPE_OBJECT)
|
||||||
|
|
||||||
|
void
|
||||||
|
grd_image_view_notify_image_view_release (GrdImageView *image_view)
|
||||||
|
{
|
||||||
|
GrdImageViewClass *klass = GRD_IMAGE_VIEW_GET_CLASS (image_view);
|
||||||
|
|
||||||
|
klass->notify_image_view_release (image_view);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
grd_image_view_init (GrdImageView *image_view)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
grd_image_view_class_init (GrdImageViewClass *klass)
|
||||||
|
{
|
||||||
|
}
|
||||||
35
grd-image-view.h
Normal file
35
grd-image-view.h
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2024 Pascal Nowack
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License as
|
||||||
|
* published by the Free Software Foundation; either version 2 of the
|
||||||
|
* License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
|
||||||
|
* 02111-1307, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <glib-object.h>
|
||||||
|
|
||||||
|
#define GRD_TYPE_IMAGE_VIEW (grd_image_view_get_type ())
|
||||||
|
G_DECLARE_DERIVABLE_TYPE (GrdImageView, grd_image_view,
|
||||||
|
GRD, IMAGE_VIEW, GObject)
|
||||||
|
|
||||||
|
struct _GrdImageViewClass
|
||||||
|
{
|
||||||
|
GObjectClass parent_class;
|
||||||
|
|
||||||
|
void (* notify_image_view_release) (GrdImageView *image_view);
|
||||||
|
};
|
||||||
|
|
||||||
|
void grd_image_view_notify_image_view_release (GrdImageView *image_view);
|
||||||
97
grd-local-buffer-copy.c
Normal file
97
grd-local-buffer-copy.c
Normal file
@@ -0,0 +1,97 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2025 Pascal Nowack
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License as
|
||||||
|
* published by the Free Software Foundation; either version 2 of the
|
||||||
|
* License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
|
||||||
|
* 02111-1307, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
|
#include "grd-local-buffer-copy.h"
|
||||||
|
|
||||||
|
struct _GrdLocalBufferCopy
|
||||||
|
{
|
||||||
|
GrdLocalBuffer parent;
|
||||||
|
|
||||||
|
uint8_t *buffer;
|
||||||
|
uint32_t buffer_stride;
|
||||||
|
};
|
||||||
|
|
||||||
|
G_DEFINE_TYPE (GrdLocalBufferCopy, grd_local_buffer_copy,
|
||||||
|
GRD_TYPE_LOCAL_BUFFER)
|
||||||
|
|
||||||
|
static uint8_t *
|
||||||
|
grd_local_buffer_copy_get_buffer (GrdLocalBuffer *local_buffer)
|
||||||
|
{
|
||||||
|
GrdLocalBufferCopy *local_buffer_copy = GRD_LOCAL_BUFFER_COPY (local_buffer);
|
||||||
|
|
||||||
|
return local_buffer_copy->buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint32_t
|
||||||
|
grd_local_buffer_copy_get_buffer_stride (GrdLocalBuffer *local_buffer)
|
||||||
|
{
|
||||||
|
GrdLocalBufferCopy *local_buffer_copy = GRD_LOCAL_BUFFER_COPY (local_buffer);
|
||||||
|
|
||||||
|
return local_buffer_copy->buffer_stride;
|
||||||
|
}
|
||||||
|
|
||||||
|
GrdLocalBufferCopy *
|
||||||
|
grd_local_buffer_copy_new (uint32_t buffer_width,
|
||||||
|
uint32_t buffer_height)
|
||||||
|
{
|
||||||
|
GrdLocalBufferCopy *local_buffer_copy;
|
||||||
|
uint32_t buffer_stride;
|
||||||
|
|
||||||
|
g_assert (buffer_width > 0);
|
||||||
|
g_assert (buffer_height > 0);
|
||||||
|
|
||||||
|
local_buffer_copy = g_object_new (GRD_TYPE_LOCAL_BUFFER_COPY, NULL);
|
||||||
|
|
||||||
|
buffer_stride = buffer_width * 4;
|
||||||
|
|
||||||
|
local_buffer_copy->buffer = g_new0 (uint8_t, buffer_stride * buffer_height);
|
||||||
|
local_buffer_copy->buffer_stride = buffer_stride;
|
||||||
|
|
||||||
|
return local_buffer_copy;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
grd_local_buffer_copy_dispose (GObject *object)
|
||||||
|
{
|
||||||
|
GrdLocalBufferCopy *local_buffer_copy = GRD_LOCAL_BUFFER_COPY (object);
|
||||||
|
|
||||||
|
g_clear_pointer (&local_buffer_copy->buffer, g_free);
|
||||||
|
|
||||||
|
G_OBJECT_CLASS (grd_local_buffer_copy_parent_class)->dispose (object);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
grd_local_buffer_copy_init (GrdLocalBufferCopy *local_buffer)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
grd_local_buffer_copy_class_init (GrdLocalBufferCopyClass *klass)
|
||||||
|
{
|
||||||
|
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
||||||
|
GrdLocalBufferClass *local_buffer_class = GRD_LOCAL_BUFFER_CLASS (klass);
|
||||||
|
|
||||||
|
object_class->dispose = grd_local_buffer_copy_dispose;
|
||||||
|
|
||||||
|
local_buffer_class->get_buffer = grd_local_buffer_copy_get_buffer;
|
||||||
|
local_buffer_class->get_buffer_stride =
|
||||||
|
grd_local_buffer_copy_get_buffer_stride;
|
||||||
|
}
|
||||||
29
grd-local-buffer-copy.h
Normal file
29
grd-local-buffer-copy.h
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2025 Pascal Nowack
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License as
|
||||||
|
* published by the Free Software Foundation; either version 2 of the
|
||||||
|
* License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
|
||||||
|
* 02111-1307, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "grd-local-buffer.h"
|
||||||
|
|
||||||
|
#define GRD_TYPE_LOCAL_BUFFER_COPY (grd_local_buffer_copy_get_type ())
|
||||||
|
G_DECLARE_FINAL_TYPE (GrdLocalBufferCopy, grd_local_buffer_copy,
|
||||||
|
GRD, LOCAL_BUFFER_COPY, GrdLocalBuffer)
|
||||||
|
|
||||||
|
GrdLocalBufferCopy *grd_local_buffer_copy_new (uint32_t buffer_width,
|
||||||
|
uint32_t buffer_height);
|
||||||
86
grd-local-buffer-wrapper-rdp.c
Normal file
86
grd-local-buffer-wrapper-rdp.c
Normal file
@@ -0,0 +1,86 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2025 Pascal Nowack
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License as
|
||||||
|
* published by the Free Software Foundation; either version 2 of the
|
||||||
|
* License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
|
||||||
|
* 02111-1307, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
|
#include "grd-local-buffer-wrapper-rdp.h"
|
||||||
|
|
||||||
|
#include "grd-rdp-buffer.h"
|
||||||
|
#include "grd-rdp-pw-buffer.h"
|
||||||
|
|
||||||
|
struct _GrdLocalBufferWrapperRdp
|
||||||
|
{
|
||||||
|
GrdLocalBuffer parent;
|
||||||
|
|
||||||
|
uint8_t *buffer;
|
||||||
|
int32_t buffer_stride;
|
||||||
|
};
|
||||||
|
|
||||||
|
G_DEFINE_TYPE (GrdLocalBufferWrapperRdp, grd_local_buffer_wrapper_rdp,
|
||||||
|
GRD_TYPE_LOCAL_BUFFER)
|
||||||
|
|
||||||
|
void
|
||||||
|
grd_local_buffer_wrapper_rdp_attach_rdp_buffer (GrdLocalBufferWrapperRdp *buffer_wrapper,
|
||||||
|
GrdRdpBuffer *rdp_buffer)
|
||||||
|
{
|
||||||
|
GrdRdpPwBuffer *rdp_pw_buffer = grd_rdp_buffer_get_rdp_pw_buffer (rdp_buffer);
|
||||||
|
|
||||||
|
buffer_wrapper->buffer =
|
||||||
|
grd_rdp_pw_buffer_get_mapped_data (rdp_pw_buffer,
|
||||||
|
&buffer_wrapper->buffer_stride);
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint8_t *
|
||||||
|
grd_local_buffer_wrapper_rdp_get_buffer (GrdLocalBuffer *local_buffer)
|
||||||
|
{
|
||||||
|
GrdLocalBufferWrapperRdp *buffer_wrapper =
|
||||||
|
GRD_LOCAL_BUFFER_WRAPPER_RDP (local_buffer);
|
||||||
|
|
||||||
|
return buffer_wrapper->buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint32_t
|
||||||
|
grd_local_buffer_wrapper_rdp_get_buffer_stride (GrdLocalBuffer *local_buffer)
|
||||||
|
{
|
||||||
|
GrdLocalBufferWrapperRdp *buffer_wrapper =
|
||||||
|
GRD_LOCAL_BUFFER_WRAPPER_RDP (local_buffer);
|
||||||
|
|
||||||
|
return buffer_wrapper->buffer_stride;
|
||||||
|
}
|
||||||
|
|
||||||
|
GrdLocalBufferWrapperRdp *
|
||||||
|
grd_local_buffer_wrapper_rdp_new (void)
|
||||||
|
{
|
||||||
|
return g_object_new (GRD_TYPE_LOCAL_BUFFER_WRAPPER_RDP, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
grd_local_buffer_wrapper_rdp_init (GrdLocalBufferWrapperRdp *local_buffer)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
grd_local_buffer_wrapper_rdp_class_init (GrdLocalBufferWrapperRdpClass *klass)
|
||||||
|
{
|
||||||
|
GrdLocalBufferClass *local_buffer_class = GRD_LOCAL_BUFFER_CLASS (klass);
|
||||||
|
|
||||||
|
local_buffer_class->get_buffer = grd_local_buffer_wrapper_rdp_get_buffer;
|
||||||
|
local_buffer_class->get_buffer_stride =
|
||||||
|
grd_local_buffer_wrapper_rdp_get_buffer_stride;
|
||||||
|
}
|
||||||
32
grd-local-buffer-wrapper-rdp.h
Normal file
32
grd-local-buffer-wrapper-rdp.h
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2025 Pascal Nowack
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License as
|
||||||
|
* published by the Free Software Foundation; either version 2 of the
|
||||||
|
* License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
|
||||||
|
* 02111-1307, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "grd-local-buffer.h"
|
||||||
|
#include "grd-types.h"
|
||||||
|
|
||||||
|
#define GRD_TYPE_LOCAL_BUFFER_WRAPPER_RDP (grd_local_buffer_wrapper_rdp_get_type ())
|
||||||
|
G_DECLARE_FINAL_TYPE (GrdLocalBufferWrapperRdp, grd_local_buffer_wrapper_rdp,
|
||||||
|
GRD, LOCAL_BUFFER_WRAPPER_RDP, GrdLocalBuffer)
|
||||||
|
|
||||||
|
GrdLocalBufferWrapperRdp *grd_local_buffer_wrapper_rdp_new (void);
|
||||||
|
|
||||||
|
void grd_local_buffer_wrapper_rdp_attach_rdp_buffer (GrdLocalBufferWrapperRdp *buffer_wrapper,
|
||||||
|
GrdRdpBuffer *rdp_buffer);
|
||||||
51
grd-local-buffer.c
Normal file
51
grd-local-buffer.c
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2024 Pascal Nowack
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License as
|
||||||
|
* published by the Free Software Foundation; either version 2 of the
|
||||||
|
* License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
|
||||||
|
* 02111-1307, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
|
#include "grd-local-buffer.h"
|
||||||
|
|
||||||
|
G_DEFINE_ABSTRACT_TYPE (GrdLocalBuffer, grd_local_buffer,
|
||||||
|
G_TYPE_OBJECT)
|
||||||
|
|
||||||
|
uint8_t *
|
||||||
|
grd_local_buffer_get_buffer (GrdLocalBuffer *local_buffer)
|
||||||
|
{
|
||||||
|
GrdLocalBufferClass *klass = GRD_LOCAL_BUFFER_GET_CLASS (local_buffer);
|
||||||
|
|
||||||
|
return klass->get_buffer (local_buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t
|
||||||
|
grd_local_buffer_get_buffer_stride (GrdLocalBuffer *local_buffer)
|
||||||
|
{
|
||||||
|
GrdLocalBufferClass *klass = GRD_LOCAL_BUFFER_GET_CLASS (local_buffer);
|
||||||
|
|
||||||
|
return klass->get_buffer_stride (local_buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
grd_local_buffer_init (GrdLocalBuffer *local_buffer)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
grd_local_buffer_class_init (GrdLocalBufferClass *klass)
|
||||||
|
{
|
||||||
|
}
|
||||||
39
grd-local-buffer.h
Normal file
39
grd-local-buffer.h
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2024 Pascal Nowack
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License as
|
||||||
|
* published by the Free Software Foundation; either version 2 of the
|
||||||
|
* License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
|
||||||
|
* 02111-1307, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <glib-object.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#define GRD_TYPE_LOCAL_BUFFER (grd_local_buffer_get_type ())
|
||||||
|
G_DECLARE_DERIVABLE_TYPE (GrdLocalBuffer, grd_local_buffer,
|
||||||
|
GRD, LOCAL_BUFFER, GObject)
|
||||||
|
|
||||||
|
struct _GrdLocalBufferClass
|
||||||
|
{
|
||||||
|
GObjectClass parent_class;
|
||||||
|
|
||||||
|
uint8_t *(* get_buffer) (GrdLocalBuffer *local_buffer);
|
||||||
|
uint32_t (* get_buffer_stride) (GrdLocalBuffer *local_buffer);
|
||||||
|
};
|
||||||
|
|
||||||
|
uint8_t *grd_local_buffer_get_buffer (GrdLocalBuffer *local_buffer);
|
||||||
|
|
||||||
|
uint32_t grd_local_buffer_get_buffer_stride (GrdLocalBuffer *local_buffer);
|
||||||
87
grd-mime-type.c
Normal file
87
grd-mime-type.c
Normal file
@@ -0,0 +1,87 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2020 Pascal Nowack
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License as
|
||||||
|
* published by the Free Software Foundation; either version 2 of the
|
||||||
|
* License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
|
||||||
|
* 02111-1307, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
|
#include "grd-mime-type.h"
|
||||||
|
|
||||||
|
#include <gio/gio.h>
|
||||||
|
|
||||||
|
const char *
|
||||||
|
grd_mime_type_to_string (GrdMimeType mime_type)
|
||||||
|
{
|
||||||
|
switch (mime_type)
|
||||||
|
{
|
||||||
|
case GRD_MIME_TYPE_TEXT_PLAIN:
|
||||||
|
return "text/plain";
|
||||||
|
case GRD_MIME_TYPE_TEXT_PLAIN_UTF8:
|
||||||
|
return "text/plain;charset=utf-8";
|
||||||
|
case GRD_MIME_TYPE_TEXT_UTF8_STRING:
|
||||||
|
return "UTF8_STRING";
|
||||||
|
case GRD_MIME_TYPE_TEXT_HTML:
|
||||||
|
return "text/html";
|
||||||
|
case GRD_MIME_TYPE_IMAGE_BMP:
|
||||||
|
return "image/bmp";
|
||||||
|
case GRD_MIME_TYPE_IMAGE_TIFF:
|
||||||
|
return "image/tiff";
|
||||||
|
case GRD_MIME_TYPE_IMAGE_GIF:
|
||||||
|
return "image/gif";
|
||||||
|
case GRD_MIME_TYPE_IMAGE_JPEG:
|
||||||
|
return "image/jpeg";
|
||||||
|
case GRD_MIME_TYPE_IMAGE_PNG:
|
||||||
|
return "image/png";
|
||||||
|
case GRD_MIME_TYPE_TEXT_URILIST:
|
||||||
|
return "text/uri-list";
|
||||||
|
case GRD_MIME_TYPE_XS_GNOME_COPIED_FILES:
|
||||||
|
return "x-special/gnome-copied-files";
|
||||||
|
default:
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
g_assert_not_reached ();
|
||||||
|
}
|
||||||
|
|
||||||
|
GrdMimeType
|
||||||
|
grd_mime_type_from_string (const char *mime_type_string)
|
||||||
|
{
|
||||||
|
if (strcmp (mime_type_string, "text/plain") == 0)
|
||||||
|
return GRD_MIME_TYPE_TEXT_PLAIN;
|
||||||
|
else if (strcmp (mime_type_string, "text/plain;charset=utf-8") == 0)
|
||||||
|
return GRD_MIME_TYPE_TEXT_PLAIN_UTF8;
|
||||||
|
else if (strcmp (mime_type_string, "UTF8_STRING") == 0)
|
||||||
|
return GRD_MIME_TYPE_TEXT_UTF8_STRING;
|
||||||
|
else if (strcmp (mime_type_string, "text/html") == 0)
|
||||||
|
return GRD_MIME_TYPE_TEXT_HTML;
|
||||||
|
else if (strcmp (mime_type_string, "image/bmp") == 0)
|
||||||
|
return GRD_MIME_TYPE_IMAGE_BMP;
|
||||||
|
else if (strcmp (mime_type_string, "image/tiff") == 0)
|
||||||
|
return GRD_MIME_TYPE_IMAGE_TIFF;
|
||||||
|
else if (strcmp (mime_type_string, "image/gif") == 0)
|
||||||
|
return GRD_MIME_TYPE_IMAGE_GIF;
|
||||||
|
else if (strcmp (mime_type_string, "image/jpeg") == 0)
|
||||||
|
return GRD_MIME_TYPE_IMAGE_JPEG;
|
||||||
|
else if (strcmp (mime_type_string, "image/png") == 0)
|
||||||
|
return GRD_MIME_TYPE_IMAGE_PNG;
|
||||||
|
else if (strcmp (mime_type_string, "text/uri-list") == 0)
|
||||||
|
return GRD_MIME_TYPE_TEXT_URILIST;
|
||||||
|
else if (strcmp (mime_type_string, "x-special/gnome-copied-files") == 0)
|
||||||
|
return GRD_MIME_TYPE_XS_GNOME_COPIED_FILES;
|
||||||
|
|
||||||
|
return GRD_MIME_TYPE_NONE;
|
||||||
|
}
|
||||||
52
grd-mime-type.h
Normal file
52
grd-mime-type.h
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2020 Pascal Nowack
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License as
|
||||||
|
* published by the Free Software Foundation; either version 2 of the
|
||||||
|
* License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
|
||||||
|
* 02111-1307, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
typedef enum _GrdMimeType
|
||||||
|
{
|
||||||
|
GRD_MIME_TYPE_NONE,
|
||||||
|
GRD_MIME_TYPE_TEXT_PLAIN, /* text/plain */
|
||||||
|
GRD_MIME_TYPE_TEXT_PLAIN_UTF8, /* text/plain;charset=utf-8 */
|
||||||
|
GRD_MIME_TYPE_TEXT_UTF8_STRING, /* UTF8_STRING */
|
||||||
|
GRD_MIME_TYPE_TEXT_HTML, /* text/html */
|
||||||
|
GRD_MIME_TYPE_IMAGE_BMP, /* image/bmp */
|
||||||
|
GRD_MIME_TYPE_IMAGE_TIFF, /* image/tiff */
|
||||||
|
GRD_MIME_TYPE_IMAGE_GIF, /* image/gif */
|
||||||
|
GRD_MIME_TYPE_IMAGE_JPEG, /* image/jpeg */
|
||||||
|
GRD_MIME_TYPE_IMAGE_PNG, /* image/png */
|
||||||
|
GRD_MIME_TYPE_TEXT_URILIST, /* text/uri-list */
|
||||||
|
GRD_MIME_TYPE_XS_GNOME_COPIED_FILES, /* x-special/gnome-copied-files */
|
||||||
|
} GrdMimeType;
|
||||||
|
|
||||||
|
typedef struct _GrdMimeTypeTable
|
||||||
|
{
|
||||||
|
GrdMimeType mime_type;
|
||||||
|
|
||||||
|
struct
|
||||||
|
{
|
||||||
|
uint32_t format_id;
|
||||||
|
} rdp;
|
||||||
|
} GrdMimeTypeTable;
|
||||||
|
|
||||||
|
const char *grd_mime_type_to_string (GrdMimeType mime_type);
|
||||||
|
|
||||||
|
GrdMimeType grd_mime_type_from_string (const char *mime_type_string);
|
||||||
886
grd-nal-writer.c
Normal file
886
grd-nal-writer.c
Normal file
@@ -0,0 +1,886 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2022 Pascal Nowack
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License as
|
||||||
|
* published by the Free Software Foundation; either version 2 of the
|
||||||
|
* License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
|
||||||
|
* 02111-1307, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
|
#include "grd-nal-writer.h"
|
||||||
|
|
||||||
|
#define H264_PROFILE_HIGH 100
|
||||||
|
|
||||||
|
/* See also E.2.1 VUI parameters semantics (Rec. ITU-T H.264 (08/2021)) */
|
||||||
|
#define H264_Extended_SAR 255
|
||||||
|
|
||||||
|
#define H264_NAL_REF_IDC_ZERO 0
|
||||||
|
#define H264_NAL_REF_IDC_MEDIUM 2
|
||||||
|
#define H264_NAL_REF_IDC_HIGH 3
|
||||||
|
|
||||||
|
/*
|
||||||
|
* See also Table 7-1 – NAL unit type codes, syntax element categories,
|
||||||
|
* and NAL unit type classes (Rec. ITU-T H.264 (08/2021))
|
||||||
|
*/
|
||||||
|
#define H264_NAL_UNIT_TYPE_SLICE_NON_IDR 1
|
||||||
|
#define H264_NAL_UNIT_TYPE_SLICE_IDR 5
|
||||||
|
#define H264_NAL_UNIT_TYPE_SPS 7
|
||||||
|
#define H264_NAL_UNIT_TYPE_PPS 8
|
||||||
|
#define H264_NAL_UNIT_TYPE_AUD 9
|
||||||
|
|
||||||
|
/*
|
||||||
|
* See also Table 7-6 – Name association to slice_type
|
||||||
|
* (Rec. ITU-T H.264 (08/2021))
|
||||||
|
*/
|
||||||
|
#define H264_SLICE_TYPE_P 0
|
||||||
|
#define H264_SLICE_TYPE_B 1
|
||||||
|
#define H264_SLICE_TYPE_I 2
|
||||||
|
|
||||||
|
#define BITSTREAM_ALLOCATION_STEP 4096
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
uint32_t *buffer;
|
||||||
|
uint32_t bit_offset;
|
||||||
|
uint32_t capacity;
|
||||||
|
} NalBitstream;
|
||||||
|
|
||||||
|
struct _GrdNalWriter
|
||||||
|
{
|
||||||
|
GObject parent;
|
||||||
|
|
||||||
|
NalBitstream *nal_bitstream;
|
||||||
|
};
|
||||||
|
|
||||||
|
G_DEFINE_TYPE (GrdNalWriter, grd_nal_writer, G_TYPE_OBJECT)
|
||||||
|
|
||||||
|
static void
|
||||||
|
start_bitstream (GrdNalWriter *nal_writer)
|
||||||
|
{
|
||||||
|
g_assert (!nal_writer->nal_bitstream);
|
||||||
|
|
||||||
|
nal_writer->nal_bitstream = g_new0 (NalBitstream, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint32_t
|
||||||
|
swap_byte_order (uint32_t value)
|
||||||
|
{
|
||||||
|
uint8_t *ptr = (uint8_t *) &value;
|
||||||
|
|
||||||
|
return ptr[0] << 24 | ptr[1] << 16 | ptr[2] << 8 | ptr[3];
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint8_t *
|
||||||
|
end_bitstream (GrdNalWriter *nal_writer,
|
||||||
|
uint32_t *bitstream_length)
|
||||||
|
{
|
||||||
|
NalBitstream *nal_bitstream = nal_writer->nal_bitstream;
|
||||||
|
uint32_t *buffer = nal_bitstream->buffer;
|
||||||
|
uint32_t offset_in_dword;
|
||||||
|
uint32_t byte_pos;
|
||||||
|
uint32_t bits_left;
|
||||||
|
|
||||||
|
byte_pos = nal_bitstream->bit_offset >> 5;
|
||||||
|
offset_in_dword = nal_bitstream->bit_offset & 0x1F;
|
||||||
|
bits_left = 32 - offset_in_dword;
|
||||||
|
|
||||||
|
if (bits_left)
|
||||||
|
buffer[byte_pos] = swap_byte_order (buffer[byte_pos] << bits_left);
|
||||||
|
|
||||||
|
*bitstream_length = nal_bitstream->bit_offset;
|
||||||
|
g_clear_pointer (&nal_writer->nal_bitstream, g_free);
|
||||||
|
|
||||||
|
return (uint8_t *) buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
ensure_remaining_capacity (NalBitstream *nal_bitstream,
|
||||||
|
uint32_t required_capacity_in_bits)
|
||||||
|
{
|
||||||
|
uint32_t new_capacity;
|
||||||
|
|
||||||
|
if (required_capacity_in_bits <= nal_bitstream->capacity * 32)
|
||||||
|
return;
|
||||||
|
|
||||||
|
new_capacity = nal_bitstream->capacity + BITSTREAM_ALLOCATION_STEP;
|
||||||
|
g_assert (required_capacity_in_bits <= new_capacity * 32);
|
||||||
|
|
||||||
|
nal_bitstream->buffer = g_realloc (nal_bitstream->buffer,
|
||||||
|
new_capacity * sizeof (uint32_t));
|
||||||
|
nal_bitstream->capacity = new_capacity;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
write_u (GrdNalWriter *nal_writer,
|
||||||
|
uint32_t value,
|
||||||
|
uint32_t n_bits)
|
||||||
|
{
|
||||||
|
NalBitstream *nal_bitstream = nal_writer->nal_bitstream;
|
||||||
|
uint32_t offset_in_dword;
|
||||||
|
uint32_t byte_pos;
|
||||||
|
uint32_t bits_left;
|
||||||
|
|
||||||
|
if (n_bits == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
ensure_remaining_capacity (nal_bitstream, nal_bitstream->bit_offset + n_bits);
|
||||||
|
|
||||||
|
byte_pos = nal_bitstream->bit_offset >> 5;
|
||||||
|
offset_in_dword = nal_bitstream->bit_offset & 0x1F;
|
||||||
|
bits_left = 32 - offset_in_dword;
|
||||||
|
|
||||||
|
if (!offset_in_dword)
|
||||||
|
nal_bitstream->buffer[byte_pos] = 0;
|
||||||
|
|
||||||
|
nal_bitstream->bit_offset += n_bits;
|
||||||
|
if (bits_left > n_bits)
|
||||||
|
{
|
||||||
|
nal_bitstream->buffer[byte_pos] <<= n_bits;
|
||||||
|
nal_bitstream->buffer[byte_pos] |= value;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
n_bits -= bits_left;
|
||||||
|
nal_bitstream->buffer[byte_pos] <<= bits_left;
|
||||||
|
nal_bitstream->buffer[byte_pos] |= value >> n_bits;
|
||||||
|
nal_bitstream->buffer[byte_pos] =
|
||||||
|
swap_byte_order (nal_bitstream->buffer[byte_pos]);
|
||||||
|
|
||||||
|
nal_bitstream->buffer[++byte_pos] = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Exponential Golomb coding (unsigned) */
|
||||||
|
static void
|
||||||
|
write_ue (GrdNalWriter *nal_writer,
|
||||||
|
uint32_t value)
|
||||||
|
{
|
||||||
|
uint32_t value_to_write;
|
||||||
|
uint32_t n_bits = 0;
|
||||||
|
uint32_t tmp;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Write down value + 1, but before that write down n - 1 zeros,
|
||||||
|
* where n represents the bits to be written for value + 1
|
||||||
|
*/
|
||||||
|
value_to_write = value + 1;
|
||||||
|
tmp = value_to_write;
|
||||||
|
while (tmp)
|
||||||
|
{
|
||||||
|
++n_bits;
|
||||||
|
tmp >>= 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (n_bits > 1)
|
||||||
|
write_u (nal_writer, 0, n_bits - 1);
|
||||||
|
write_u (nal_writer, value_to_write, n_bits);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Exponential Golomb coding (signed) */
|
||||||
|
static void
|
||||||
|
write_se (GrdNalWriter *nal_writer,
|
||||||
|
int32_t value)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* If the value is <= 0, map the value to -2 * value (even integer value),
|
||||||
|
* otherwise map it to 2 * value - 1 (odd integer value)
|
||||||
|
*/
|
||||||
|
if (value <= 0)
|
||||||
|
write_ue (nal_writer, -value << 1);
|
||||||
|
else
|
||||||
|
write_ue (nal_writer, (value << 1) - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
write_nal_start_code_prefix (GrdNalWriter *nal_writer)
|
||||||
|
{
|
||||||
|
write_u (nal_writer, 0x00000001, 32);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
write_nal_header (GrdNalWriter *nal_writer,
|
||||||
|
uint8_t nal_ref_idc,
|
||||||
|
uint8_t nal_unit_type)
|
||||||
|
{
|
||||||
|
/* See also 7.3.1 NAL unit syntax (Rec. ITU-T H.264 (08/2021)) */
|
||||||
|
/* forbidden_zero_bit */
|
||||||
|
write_u (nal_writer, 0, 1);
|
||||||
|
/* nal_ref_idc */
|
||||||
|
write_u (nal_writer, nal_ref_idc, 2);
|
||||||
|
/* nal_unit_type */
|
||||||
|
write_u (nal_writer, nal_unit_type, 5);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
byte_align_bitstream (GrdNalWriter *nal_writer)
|
||||||
|
{
|
||||||
|
NalBitstream *nal_bitstream = nal_writer->nal_bitstream;
|
||||||
|
uint32_t offset_in_byte;
|
||||||
|
uint32_t bits_left;
|
||||||
|
|
||||||
|
offset_in_byte = nal_bitstream->bit_offset & 0x7;
|
||||||
|
bits_left = 8 - offset_in_byte;
|
||||||
|
|
||||||
|
if (!bits_left)
|
||||||
|
return;
|
||||||
|
|
||||||
|
/* rbsp_alignment_zero_bit */
|
||||||
|
write_u (nal_writer, 0, bits_left);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
write_trailing_bits (GrdNalWriter *nal_writer)
|
||||||
|
{
|
||||||
|
/* rbsp_stop_one_bit */
|
||||||
|
write_u (nal_writer, 1, 1);
|
||||||
|
byte_align_bitstream (nal_writer);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
write_access_unit_delimiter (GrdNalWriter *nal_writer)
|
||||||
|
{
|
||||||
|
uint32_t primary_pic_type;
|
||||||
|
|
||||||
|
primary_pic_type = 1;
|
||||||
|
|
||||||
|
write_u (nal_writer, primary_pic_type, 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t *
|
||||||
|
grd_nal_writer_get_aud_bitstream (GrdNalWriter *nal_writer,
|
||||||
|
uint32_t *bitstream_length)
|
||||||
|
{
|
||||||
|
uint8_t *bitstream;
|
||||||
|
|
||||||
|
start_bitstream (nal_writer);
|
||||||
|
write_nal_start_code_prefix (nal_writer);
|
||||||
|
write_nal_header (nal_writer, H264_NAL_REF_IDC_ZERO, H264_NAL_UNIT_TYPE_AUD);
|
||||||
|
write_access_unit_delimiter (nal_writer);
|
||||||
|
write_trailing_bits (nal_writer);
|
||||||
|
|
||||||
|
bitstream = end_bitstream (nal_writer, bitstream_length);
|
||||||
|
g_assert (*bitstream_length % 8 == 0);
|
||||||
|
|
||||||
|
return bitstream;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
write_vui_parameters (GrdNalWriter *nal_writer,
|
||||||
|
const VAEncSequenceParameterBufferH264 *sequence_param)
|
||||||
|
{
|
||||||
|
uint32_t aspect_ratio_info_present_flag;
|
||||||
|
uint32_t overscan_info_present_flag;
|
||||||
|
uint32_t video_signal_type_present_flag;
|
||||||
|
uint32_t chroma_loc_info_present_flag;
|
||||||
|
uint32_t timing_info_present_flag;
|
||||||
|
uint32_t nal_hrd_parameters_present_flag;
|
||||||
|
uint32_t vcl_hrd_parameters_present_flag;
|
||||||
|
uint32_t pic_struct_present_flag;
|
||||||
|
uint32_t bitstream_restriction_flag;
|
||||||
|
|
||||||
|
g_assert (sequence_param->vui_parameters_present_flag);
|
||||||
|
|
||||||
|
aspect_ratio_info_present_flag =
|
||||||
|
sequence_param->vui_fields.bits.aspect_ratio_info_present_flag;
|
||||||
|
timing_info_present_flag =
|
||||||
|
sequence_param->vui_fields.bits.timing_info_present_flag;
|
||||||
|
bitstream_restriction_flag =
|
||||||
|
sequence_param->vui_fields.bits.bitstream_restriction_flag;
|
||||||
|
|
||||||
|
overscan_info_present_flag = 0;
|
||||||
|
video_signal_type_present_flag = 0;
|
||||||
|
chroma_loc_info_present_flag = 0;
|
||||||
|
nal_hrd_parameters_present_flag = 0;
|
||||||
|
vcl_hrd_parameters_present_flag = 0;
|
||||||
|
pic_struct_present_flag = 0;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* See also E.1.1 VUI parameters syntax (Rec. ITU-T H.264 (08/2021))
|
||||||
|
*
|
||||||
|
* Not all paths are covered, only the ones relevant for GNOME Remote Desktop.
|
||||||
|
* Unhandled branches are preceded with an assertion.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* aspect_ratio_info_present_flag */
|
||||||
|
write_u (nal_writer, aspect_ratio_info_present_flag, 1);
|
||||||
|
if (aspect_ratio_info_present_flag)
|
||||||
|
{
|
||||||
|
/* aspect_ratio_idc */
|
||||||
|
write_u (nal_writer, sequence_param->aspect_ratio_idc, 8);
|
||||||
|
if (sequence_param->aspect_ratio_idc == H264_Extended_SAR)
|
||||||
|
{
|
||||||
|
/* sar_width */
|
||||||
|
write_u (nal_writer, sequence_param->sar_width, 16);
|
||||||
|
/* sar_height */
|
||||||
|
write_u (nal_writer, sequence_param->sar_height, 16);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* overscan_info_present_flag */
|
||||||
|
write_u (nal_writer, overscan_info_present_flag, 1);
|
||||||
|
g_assert (!overscan_info_present_flag);
|
||||||
|
|
||||||
|
/* video_signal_type_present_flag */
|
||||||
|
write_u (nal_writer, video_signal_type_present_flag, 1);
|
||||||
|
g_assert (!video_signal_type_present_flag);
|
||||||
|
|
||||||
|
/* chroma_loc_info_present_flag */
|
||||||
|
write_u (nal_writer, chroma_loc_info_present_flag, 1);
|
||||||
|
g_assert (!chroma_loc_info_present_flag);
|
||||||
|
|
||||||
|
/* timing_info_present_flag */
|
||||||
|
write_u (nal_writer, timing_info_present_flag, 1);
|
||||||
|
if (timing_info_present_flag)
|
||||||
|
{
|
||||||
|
uint32_t fixed_frame_rate_flag =
|
||||||
|
sequence_param->vui_fields.bits.fixed_frame_rate_flag;
|
||||||
|
|
||||||
|
/* num_units_in_tick */
|
||||||
|
write_u (nal_writer, sequence_param->num_units_in_tick, 32);
|
||||||
|
/* time_scale */
|
||||||
|
write_u (nal_writer, sequence_param->time_scale, 32);
|
||||||
|
/* fixed_frame_rate_flag */
|
||||||
|
write_u (nal_writer, fixed_frame_rate_flag, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* nal_hrd_parameters_present_flag */
|
||||||
|
write_u (nal_writer, nal_hrd_parameters_present_flag, 1);
|
||||||
|
g_assert (!nal_hrd_parameters_present_flag);
|
||||||
|
|
||||||
|
/* vcl_hrd_parameters_present_flag */
|
||||||
|
write_u (nal_writer, vcl_hrd_parameters_present_flag, 1);
|
||||||
|
g_assert (!vcl_hrd_parameters_present_flag);
|
||||||
|
|
||||||
|
g_assert (!nal_hrd_parameters_present_flag &&
|
||||||
|
!vcl_hrd_parameters_present_flag);
|
||||||
|
|
||||||
|
/* pic_struct_present_flag */
|
||||||
|
write_u (nal_writer, pic_struct_present_flag, 1);
|
||||||
|
/* bitstream_restriction_flag */
|
||||||
|
write_u (nal_writer, bitstream_restriction_flag, 1);
|
||||||
|
if (bitstream_restriction_flag)
|
||||||
|
{
|
||||||
|
uint32_t motion_vectors_over_pic_boundaries_flag;
|
||||||
|
uint32_t max_bytes_per_pic_denom;
|
||||||
|
uint32_t max_bits_per_mb_denom;
|
||||||
|
uint32_t log2_max_mv_length_horizontal;
|
||||||
|
uint32_t log2_max_mv_length_vertical;
|
||||||
|
uint32_t max_num_reorder_frames;
|
||||||
|
uint32_t max_dec_frame_buffering;
|
||||||
|
|
||||||
|
log2_max_mv_length_horizontal =
|
||||||
|
sequence_param->vui_fields.bits.log2_max_mv_length_horizontal;
|
||||||
|
log2_max_mv_length_vertical =
|
||||||
|
sequence_param->vui_fields.bits.log2_max_mv_length_vertical;
|
||||||
|
|
||||||
|
motion_vectors_over_pic_boundaries_flag = 1;
|
||||||
|
max_bytes_per_pic_denom = 0;
|
||||||
|
max_bits_per_mb_denom = 0;
|
||||||
|
max_num_reorder_frames = 0;
|
||||||
|
max_dec_frame_buffering = 1;
|
||||||
|
|
||||||
|
/* motion_vectors_over_pic_boundaries_flag */
|
||||||
|
write_u (nal_writer, motion_vectors_over_pic_boundaries_flag, 1);
|
||||||
|
/* max_bytes_per_pic_denom */
|
||||||
|
write_ue (nal_writer, max_bytes_per_pic_denom);
|
||||||
|
/* max_bits_per_mb_denom */
|
||||||
|
write_ue (nal_writer, max_bits_per_mb_denom);
|
||||||
|
/* log2_max_mv_length_horizontal */
|
||||||
|
write_ue (nal_writer, log2_max_mv_length_horizontal);
|
||||||
|
/* log2_max_mv_length_vertical */
|
||||||
|
write_ue (nal_writer, log2_max_mv_length_vertical);
|
||||||
|
/* max_num_reorder_frames */
|
||||||
|
write_ue (nal_writer, max_num_reorder_frames);
|
||||||
|
/* max_dec_frame_buffering */
|
||||||
|
write_ue (nal_writer, max_dec_frame_buffering);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
write_sps_data (GrdNalWriter *nal_writer,
|
||||||
|
const VAEncSequenceParameterBufferH264 *sequence_param)
|
||||||
|
{
|
||||||
|
uint32_t profile_idc;
|
||||||
|
uint32_t constraint_set0_flag;
|
||||||
|
uint32_t constraint_set1_flag;
|
||||||
|
uint32_t constraint_set2_flag;
|
||||||
|
uint32_t constraint_set3_flag;
|
||||||
|
uint32_t constraint_set4_flag;
|
||||||
|
uint32_t constraint_set5_flag;
|
||||||
|
uint32_t chroma_format_idc;
|
||||||
|
uint32_t qpprime_y_zero_transform_bypass_flag;
|
||||||
|
uint32_t seq_scaling_matrix_present_flag;
|
||||||
|
uint32_t log2_max_frame_num_minus4;
|
||||||
|
uint32_t pic_order_cnt_type;
|
||||||
|
uint32_t gaps_in_frame_num_value_allowed_flag;
|
||||||
|
uint32_t pic_height_in_map_units;
|
||||||
|
uint32_t frame_mbs_only_flag;
|
||||||
|
uint32_t direct_8x8_inference_flag;
|
||||||
|
|
||||||
|
g_assert (sequence_param->picture_width_in_mbs > 0);
|
||||||
|
|
||||||
|
frame_mbs_only_flag = sequence_param->seq_fields.bits.frame_mbs_only_flag;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* See also 7.4.2.1.1 Sequence parameter set data semantics
|
||||||
|
* (Rec. ITU-T H.264 (08/2021))
|
||||||
|
*/
|
||||||
|
profile_idc = H264_PROFILE_HIGH;
|
||||||
|
constraint_set0_flag = 0;
|
||||||
|
constraint_set1_flag = 0;
|
||||||
|
constraint_set2_flag = 0;
|
||||||
|
constraint_set3_flag = 0;
|
||||||
|
|
||||||
|
g_assert (profile_idc == H264_PROFILE_HIGH);
|
||||||
|
g_assert (frame_mbs_only_flag == 1);
|
||||||
|
/* frame_mbs_only_flag is equal to 1 */
|
||||||
|
constraint_set4_flag = 1;
|
||||||
|
|
||||||
|
g_assert (profile_idc == H264_PROFILE_HIGH);
|
||||||
|
/* No B-slices are present in the coded video sequence */
|
||||||
|
constraint_set5_flag = 1;
|
||||||
|
|
||||||
|
chroma_format_idc = sequence_param->seq_fields.bits.chroma_format_idc;
|
||||||
|
seq_scaling_matrix_present_flag =
|
||||||
|
sequence_param->seq_fields.bits.seq_scaling_matrix_present_flag;
|
||||||
|
log2_max_frame_num_minus4 =
|
||||||
|
sequence_param->seq_fields.bits.log2_max_frame_num_minus4;
|
||||||
|
pic_order_cnt_type = sequence_param->seq_fields.bits.pic_order_cnt_type;
|
||||||
|
direct_8x8_inference_flag =
|
||||||
|
sequence_param->seq_fields.bits.direct_8x8_inference_flag;
|
||||||
|
|
||||||
|
qpprime_y_zero_transform_bypass_flag = 0;
|
||||||
|
gaps_in_frame_num_value_allowed_flag = 0;
|
||||||
|
|
||||||
|
g_assert (frame_mbs_only_flag);
|
||||||
|
pic_height_in_map_units = sequence_param->picture_height_in_mbs;
|
||||||
|
g_assert (pic_height_in_map_units > 0);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* See also 7.3.2.1.1 Sequence parameter set data syntax
|
||||||
|
* (Rec. ITU-T H.264 (08/2021))
|
||||||
|
*
|
||||||
|
* Not all paths are covered, only the ones relevant for GNOME Remote Desktop.
|
||||||
|
* Unhandled branches are preceded with an assertion.
|
||||||
|
*/
|
||||||
|
/* profile_idc */
|
||||||
|
write_u (nal_writer, profile_idc, 8);
|
||||||
|
/* constraint_set0_flag */
|
||||||
|
write_u (nal_writer, constraint_set0_flag, 1);
|
||||||
|
/* constraint_set1_flag */
|
||||||
|
write_u (nal_writer, constraint_set1_flag, 1);
|
||||||
|
/* constraint_set2_flag */
|
||||||
|
write_u (nal_writer, constraint_set2_flag, 1);
|
||||||
|
/* constraint_set3_flag */
|
||||||
|
write_u (nal_writer, constraint_set3_flag, 1);
|
||||||
|
/* constraint_set4_flag */
|
||||||
|
write_u (nal_writer, constraint_set4_flag, 1);
|
||||||
|
/* constraint_set5_flag */
|
||||||
|
write_u (nal_writer, constraint_set5_flag, 1);
|
||||||
|
/* reserved_zero_2bits */
|
||||||
|
write_u (nal_writer, 0, 2);
|
||||||
|
/* level_idc */
|
||||||
|
write_u (nal_writer, sequence_param->level_idc, 8);
|
||||||
|
/* seq_parameter_set_id */
|
||||||
|
write_ue (nal_writer, sequence_param->seq_parameter_set_id);
|
||||||
|
|
||||||
|
g_assert (profile_idc == H264_PROFILE_HIGH);
|
||||||
|
/* chroma_format_idc */
|
||||||
|
write_ue (nal_writer, chroma_format_idc);
|
||||||
|
g_assert (chroma_format_idc != 3);
|
||||||
|
|
||||||
|
/* bit_depth_luma_minus8 */
|
||||||
|
write_ue (nal_writer, sequence_param->bit_depth_luma_minus8);
|
||||||
|
/* bit_depth_chroma_minus8 */
|
||||||
|
write_ue (nal_writer, sequence_param->bit_depth_chroma_minus8);
|
||||||
|
/* qpprime_y_zero_transform_bypass_flag */
|
||||||
|
write_u (nal_writer, qpprime_y_zero_transform_bypass_flag, 1);
|
||||||
|
/* seq_scaling_matrix_present_flag */
|
||||||
|
write_u (nal_writer, seq_scaling_matrix_present_flag, 1);
|
||||||
|
g_assert (!seq_scaling_matrix_present_flag);
|
||||||
|
|
||||||
|
/* log2_max_frame_num_minus4 */
|
||||||
|
write_ue (nal_writer, log2_max_frame_num_minus4);
|
||||||
|
/* pic_order_cnt_type */
|
||||||
|
write_ue (nal_writer, pic_order_cnt_type);
|
||||||
|
if (pic_order_cnt_type == 0)
|
||||||
|
g_assert_not_reached ();
|
||||||
|
else if (pic_order_cnt_type == 1)
|
||||||
|
g_assert_not_reached ();
|
||||||
|
|
||||||
|
/* max_num_ref_frames */
|
||||||
|
write_ue (nal_writer, sequence_param->max_num_ref_frames);
|
||||||
|
/* gaps_in_frame_num_value_allowed_flag */
|
||||||
|
write_u (nal_writer, gaps_in_frame_num_value_allowed_flag, 1);
|
||||||
|
/* pic_width_in_mbs_minus1 */
|
||||||
|
write_ue (nal_writer, sequence_param->picture_width_in_mbs - 1);
|
||||||
|
/* pic_height_in_map_units_minus1 */
|
||||||
|
write_ue (nal_writer, pic_height_in_map_units - 1);
|
||||||
|
/* frame_mbs_only_flag */
|
||||||
|
write_u (nal_writer, frame_mbs_only_flag, 1);
|
||||||
|
g_assert (frame_mbs_only_flag);
|
||||||
|
|
||||||
|
/* direct_8x8_inference_flag */
|
||||||
|
write_u (nal_writer, direct_8x8_inference_flag, 1);
|
||||||
|
/* frame_cropping_flag */
|
||||||
|
write_u (nal_writer, sequence_param->frame_cropping_flag, 1);
|
||||||
|
g_assert (!sequence_param->frame_cropping_flag);
|
||||||
|
|
||||||
|
/* vui_parameters_present_flag */
|
||||||
|
write_u (nal_writer, sequence_param->vui_parameters_present_flag, 1);
|
||||||
|
if (sequence_param->vui_parameters_present_flag)
|
||||||
|
write_vui_parameters (nal_writer, sequence_param);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t *
|
||||||
|
grd_nal_writer_get_sps_bitstream (GrdNalWriter *nal_writer,
|
||||||
|
const VAEncSequenceParameterBufferH264 *sequence_param,
|
||||||
|
uint32_t *bitstream_length)
|
||||||
|
{
|
||||||
|
uint8_t *bitstream;
|
||||||
|
|
||||||
|
start_bitstream (nal_writer);
|
||||||
|
write_nal_start_code_prefix (nal_writer);
|
||||||
|
write_nal_header (nal_writer, H264_NAL_REF_IDC_HIGH, H264_NAL_UNIT_TYPE_SPS);
|
||||||
|
write_sps_data (nal_writer, sequence_param);
|
||||||
|
write_trailing_bits (nal_writer);
|
||||||
|
|
||||||
|
bitstream = end_bitstream (nal_writer, bitstream_length);
|
||||||
|
g_assert (*bitstream_length % 8 == 0);
|
||||||
|
|
||||||
|
return bitstream;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
write_pps_data (GrdNalWriter *nal_writer,
|
||||||
|
const VAEncPictureParameterBufferH264 *picture_param)
|
||||||
|
{
|
||||||
|
uint32_t entropy_coding_mode_flag;
|
||||||
|
uint32_t bottom_field_pic_order_in_frame_present_flag;
|
||||||
|
uint32_t num_slice_groups_minus1;
|
||||||
|
uint32_t weighted_pred_flag;
|
||||||
|
uint32_t weighted_bipred_idc;
|
||||||
|
uint32_t pic_init_qs_minus26;
|
||||||
|
uint32_t deblocking_filter_control_present_flag;
|
||||||
|
uint32_t constrained_intra_pred_flag;
|
||||||
|
uint32_t redundant_pic_cnt_present_flag;
|
||||||
|
uint32_t transform_8x8_mode_flag;
|
||||||
|
uint32_t pic_scaling_matrix_present_flag;
|
||||||
|
|
||||||
|
entropy_coding_mode_flag =
|
||||||
|
picture_param->pic_fields.bits.entropy_coding_mode_flag;
|
||||||
|
bottom_field_pic_order_in_frame_present_flag =
|
||||||
|
picture_param->pic_fields.bits.pic_order_present_flag;
|
||||||
|
weighted_pred_flag = picture_param->pic_fields.bits.weighted_pred_flag;
|
||||||
|
weighted_bipred_idc = picture_param->pic_fields.bits.weighted_bipred_idc;
|
||||||
|
deblocking_filter_control_present_flag =
|
||||||
|
picture_param->pic_fields.bits.deblocking_filter_control_present_flag;
|
||||||
|
constrained_intra_pred_flag =
|
||||||
|
picture_param->pic_fields.bits.constrained_intra_pred_flag;
|
||||||
|
redundant_pic_cnt_present_flag =
|
||||||
|
picture_param->pic_fields.bits.redundant_pic_cnt_present_flag;
|
||||||
|
transform_8x8_mode_flag =
|
||||||
|
picture_param->pic_fields.bits.transform_8x8_mode_flag;
|
||||||
|
pic_scaling_matrix_present_flag =
|
||||||
|
picture_param->pic_fields.bits.pic_scaling_matrix_present_flag;
|
||||||
|
|
||||||
|
num_slice_groups_minus1 = 0;
|
||||||
|
pic_init_qs_minus26 = 0;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* See also 7.3.2.2 Picture parameter set RBSP syntax
|
||||||
|
* (Rec. ITU-T H.264 (08/2021))
|
||||||
|
*
|
||||||
|
* Not all paths are covered, only the ones relevant for GNOME Remote Desktop.
|
||||||
|
* Unhandled branches are preceded with an assertion.
|
||||||
|
*/
|
||||||
|
/* pic_parameter_set_id */
|
||||||
|
write_ue (nal_writer, picture_param->pic_parameter_set_id);
|
||||||
|
/* seq_parameter_set_id */
|
||||||
|
write_ue (nal_writer, picture_param->seq_parameter_set_id);
|
||||||
|
/* entropy_coding_mode_flag */
|
||||||
|
write_u (nal_writer, entropy_coding_mode_flag, 1);
|
||||||
|
/* bottom_field_pic_order_in_frame_present_flag */
|
||||||
|
write_u (nal_writer, bottom_field_pic_order_in_frame_present_flag, 1);
|
||||||
|
/* num_slice_groups_minus1 */
|
||||||
|
write_ue (nal_writer, num_slice_groups_minus1);
|
||||||
|
g_assert (num_slice_groups_minus1 == 0);
|
||||||
|
|
||||||
|
/* num_ref_idx_l0_default_active_minus1 */
|
||||||
|
write_ue (nal_writer, picture_param->num_ref_idx_l0_active_minus1);
|
||||||
|
/* num_ref_idx_l1_default_active_minus1 */
|
||||||
|
write_ue (nal_writer, picture_param->num_ref_idx_l1_active_minus1);
|
||||||
|
/* weighted_pred_flag */
|
||||||
|
write_u (nal_writer, weighted_pred_flag, 1);
|
||||||
|
/* weighted_bipred_idc */
|
||||||
|
write_u (nal_writer, weighted_bipred_idc, 2);
|
||||||
|
/* pic_init_qp_minus26 */
|
||||||
|
write_se (nal_writer, picture_param->pic_init_qp - 26);
|
||||||
|
/* pic_init_qs_minus26 */
|
||||||
|
write_se (nal_writer, pic_init_qs_minus26);
|
||||||
|
/* chroma_qp_index_offset */
|
||||||
|
write_se (nal_writer, picture_param->chroma_qp_index_offset);
|
||||||
|
/* deblocking_filter_control_present_flag */
|
||||||
|
write_u (nal_writer, deblocking_filter_control_present_flag, 1);
|
||||||
|
/* constrained_intra_pred_flag */
|
||||||
|
write_u (nal_writer, constrained_intra_pred_flag, 1);
|
||||||
|
/* redundant_pic_cnt_present_flag */
|
||||||
|
write_u (nal_writer, redundant_pic_cnt_present_flag, 1);
|
||||||
|
|
||||||
|
/* more_rbsp_data */
|
||||||
|
/* transform_8x8_mode_flag */
|
||||||
|
write_u (nal_writer, transform_8x8_mode_flag, 1);
|
||||||
|
/* pic_scaling_matrix_present_flag */
|
||||||
|
write_u (nal_writer, pic_scaling_matrix_present_flag, 1);
|
||||||
|
g_assert (!pic_scaling_matrix_present_flag);
|
||||||
|
|
||||||
|
/* second_chroma_qp_index_offset */
|
||||||
|
write_se (nal_writer, picture_param->second_chroma_qp_index_offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t *
|
||||||
|
grd_nal_writer_get_pps_bitstream (GrdNalWriter *nal_writer,
|
||||||
|
const VAEncPictureParameterBufferH264 *picture_param,
|
||||||
|
uint32_t *bitstream_length)
|
||||||
|
{
|
||||||
|
uint8_t *bitstream;
|
||||||
|
|
||||||
|
start_bitstream (nal_writer);
|
||||||
|
write_nal_start_code_prefix (nal_writer);
|
||||||
|
write_nal_header (nal_writer, H264_NAL_REF_IDC_HIGH, H264_NAL_UNIT_TYPE_PPS);
|
||||||
|
write_pps_data (nal_writer, picture_param);
|
||||||
|
write_trailing_bits (nal_writer);
|
||||||
|
|
||||||
|
bitstream = end_bitstream (nal_writer, bitstream_length);
|
||||||
|
g_assert (*bitstream_length % 8 == 0);
|
||||||
|
|
||||||
|
return bitstream;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
write_ref_pic_list_modification (GrdNalWriter *nal_writer,
|
||||||
|
const VAEncSliceParameterBufferH264 *slice_param)
|
||||||
|
{
|
||||||
|
if (slice_param->slice_type != H264_SLICE_TYPE_I)
|
||||||
|
{
|
||||||
|
uint32_t ref_pic_list_modification_flag_l0;
|
||||||
|
|
||||||
|
ref_pic_list_modification_flag_l0 = 0;
|
||||||
|
|
||||||
|
/* ref_pic_list_modification_flag_l0 */
|
||||||
|
write_u (nal_writer, ref_pic_list_modification_flag_l0, 1);
|
||||||
|
g_assert (!ref_pic_list_modification_flag_l0);
|
||||||
|
}
|
||||||
|
g_assert (slice_param->slice_type != H264_SLICE_TYPE_B);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
write_dec_ref_pic_marking (GrdNalWriter *nal_writer,
|
||||||
|
const VAEncPictureParameterBufferH264 *picture_param)
|
||||||
|
{
|
||||||
|
if (picture_param->pic_fields.bits.idr_pic_flag)
|
||||||
|
{
|
||||||
|
uint32_t no_output_of_prior_pics_flag;
|
||||||
|
uint32_t long_term_reference_flag;
|
||||||
|
|
||||||
|
no_output_of_prior_pics_flag = 0;
|
||||||
|
long_term_reference_flag = 0;
|
||||||
|
|
||||||
|
/* no_output_of_prior_pics_flag */
|
||||||
|
write_u (nal_writer, no_output_of_prior_pics_flag, 1);
|
||||||
|
/* long_term_reference_flag */
|
||||||
|
write_u (nal_writer, long_term_reference_flag, 1);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
uint32_t adaptive_ref_pic_marking_mode_flag;
|
||||||
|
|
||||||
|
adaptive_ref_pic_marking_mode_flag = 0;
|
||||||
|
|
||||||
|
/* adaptive_ref_pic_marking_mode_flag */
|
||||||
|
write_u (nal_writer, adaptive_ref_pic_marking_mode_flag, 1);
|
||||||
|
g_assert (!adaptive_ref_pic_marking_mode_flag);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
write_slice_header (GrdNalWriter *nal_writer,
|
||||||
|
const VAEncSliceParameterBufferH264 *slice_param,
|
||||||
|
const VAEncSequenceParameterBufferH264 *sequence_param,
|
||||||
|
const VAEncPictureParameterBufferH264 *picture_param,
|
||||||
|
const uint8_t nal_ref_idc)
|
||||||
|
{
|
||||||
|
uint32_t separate_colour_plane_flag;
|
||||||
|
uint32_t log2_max_frame_num;
|
||||||
|
uint16_t frame_num;
|
||||||
|
uint32_t frame_mbs_only_flag;
|
||||||
|
uint32_t pic_order_cnt_type;
|
||||||
|
uint32_t redundant_pic_cnt_present_flag;
|
||||||
|
uint32_t weighted_pred_flag;
|
||||||
|
uint32_t weighted_bipred_idc;
|
||||||
|
uint32_t entropy_coding_mode_flag;
|
||||||
|
uint32_t deblocking_filter_control_present_flag;
|
||||||
|
uint32_t num_slice_groups_minus1;
|
||||||
|
|
||||||
|
frame_num = picture_param->frame_num;
|
||||||
|
log2_max_frame_num =
|
||||||
|
sequence_param->seq_fields.bits.log2_max_frame_num_minus4 + 4;
|
||||||
|
frame_mbs_only_flag = sequence_param->seq_fields.bits.frame_mbs_only_flag;
|
||||||
|
pic_order_cnt_type = sequence_param->seq_fields.bits.pic_order_cnt_type;
|
||||||
|
redundant_pic_cnt_present_flag =
|
||||||
|
picture_param->pic_fields.bits.redundant_pic_cnt_present_flag;
|
||||||
|
weighted_pred_flag = picture_param->pic_fields.bits.weighted_pred_flag;
|
||||||
|
weighted_bipred_idc = picture_param->pic_fields.bits.weighted_bipred_idc;
|
||||||
|
entropy_coding_mode_flag =
|
||||||
|
picture_param->pic_fields.bits.entropy_coding_mode_flag;
|
||||||
|
deblocking_filter_control_present_flag =
|
||||||
|
picture_param->pic_fields.bits.deblocking_filter_control_present_flag;
|
||||||
|
|
||||||
|
separate_colour_plane_flag = 0;
|
||||||
|
num_slice_groups_minus1 = 0;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* See also 7.3.3 Slice header syntax (Rec. ITU-T H.264 (08/2021))
|
||||||
|
*
|
||||||
|
* Not all paths are covered, only the ones relevant for GNOME Remote Desktop.
|
||||||
|
* Unhandled branches are preceded with an assertion.
|
||||||
|
*/
|
||||||
|
/* first_mb_in_slice */
|
||||||
|
write_ue (nal_writer, slice_param->macroblock_address);
|
||||||
|
/* slice_type */
|
||||||
|
write_ue (nal_writer, slice_param->slice_type);
|
||||||
|
/* pic_parameter_set_id */
|
||||||
|
write_ue (nal_writer, slice_param->pic_parameter_set_id);
|
||||||
|
g_assert (!separate_colour_plane_flag);
|
||||||
|
|
||||||
|
/* frame_num */
|
||||||
|
write_u (nal_writer, frame_num, log2_max_frame_num);
|
||||||
|
g_assert (frame_mbs_only_flag);
|
||||||
|
|
||||||
|
if (picture_param->pic_fields.bits.idr_pic_flag)
|
||||||
|
{
|
||||||
|
/* idr_pic_id */
|
||||||
|
write_ue (nal_writer, slice_param->idr_pic_id);
|
||||||
|
}
|
||||||
|
if (pic_order_cnt_type == 0)
|
||||||
|
g_assert_not_reached ();
|
||||||
|
if (pic_order_cnt_type == 1)
|
||||||
|
g_assert_not_reached ();
|
||||||
|
|
||||||
|
g_assert (!redundant_pic_cnt_present_flag);
|
||||||
|
g_assert (slice_param->slice_type != H264_SLICE_TYPE_B);
|
||||||
|
|
||||||
|
if (slice_param->slice_type == H264_SLICE_TYPE_P)
|
||||||
|
{
|
||||||
|
/* num_ref_idx_active_override_flag */
|
||||||
|
write_u (nal_writer, slice_param->num_ref_idx_active_override_flag, 1);
|
||||||
|
g_assert (!slice_param->num_ref_idx_active_override_flag);
|
||||||
|
}
|
||||||
|
|
||||||
|
write_ref_pic_list_modification (nal_writer, slice_param);
|
||||||
|
|
||||||
|
g_assert (!weighted_pred_flag && !weighted_bipred_idc);
|
||||||
|
|
||||||
|
if (nal_ref_idc)
|
||||||
|
write_dec_ref_pic_marking (nal_writer, picture_param);
|
||||||
|
|
||||||
|
if (entropy_coding_mode_flag && slice_param->slice_type != H264_SLICE_TYPE_I)
|
||||||
|
{
|
||||||
|
/* cabac_init_idc */
|
||||||
|
write_ue (nal_writer, slice_param->cabac_init_idc);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* slice_qp_delta */
|
||||||
|
write_se (nal_writer, slice_param->slice_qp_delta);
|
||||||
|
|
||||||
|
g_assert (slice_param->slice_type == H264_SLICE_TYPE_I ||
|
||||||
|
slice_param->slice_type == H264_SLICE_TYPE_P);
|
||||||
|
|
||||||
|
g_assert (!deblocking_filter_control_present_flag);
|
||||||
|
g_assert (num_slice_groups_minus1 == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
get_nal_header_parameters (const VAEncSliceParameterBufferH264 *slice_param,
|
||||||
|
const VAEncPictureParameterBufferH264 *picture_param,
|
||||||
|
uint8_t *nal_ref_idc,
|
||||||
|
uint8_t *nal_unit_type)
|
||||||
|
{
|
||||||
|
switch (slice_param->slice_type)
|
||||||
|
{
|
||||||
|
case H264_SLICE_TYPE_I:
|
||||||
|
*nal_ref_idc = H264_NAL_REF_IDC_HIGH;
|
||||||
|
if (picture_param->pic_fields.bits.idr_pic_flag)
|
||||||
|
*nal_unit_type = H264_NAL_UNIT_TYPE_SLICE_IDR;
|
||||||
|
else
|
||||||
|
*nal_unit_type = H264_NAL_UNIT_TYPE_SLICE_NON_IDR;
|
||||||
|
break;
|
||||||
|
case H264_SLICE_TYPE_P:
|
||||||
|
*nal_ref_idc = H264_NAL_REF_IDC_MEDIUM;
|
||||||
|
*nal_unit_type = H264_NAL_UNIT_TYPE_SLICE_NON_IDR;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
g_assert_not_reached ();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t *
|
||||||
|
grd_nal_writer_get_slice_header_bitstream (GrdNalWriter *nal_writer,
|
||||||
|
const VAEncSliceParameterBufferH264 *slice_param,
|
||||||
|
const VAEncSequenceParameterBufferH264 *sequence_param,
|
||||||
|
const VAEncPictureParameterBufferH264 *picture_param,
|
||||||
|
uint32_t *bitstream_length)
|
||||||
|
{
|
||||||
|
uint8_t nal_ref_idc;
|
||||||
|
uint8_t nal_unit_type;
|
||||||
|
|
||||||
|
get_nal_header_parameters (slice_param, picture_param,
|
||||||
|
&nal_ref_idc, &nal_unit_type);
|
||||||
|
|
||||||
|
start_bitstream (nal_writer);
|
||||||
|
write_nal_start_code_prefix (nal_writer);
|
||||||
|
write_nal_header (nal_writer, nal_ref_idc, nal_unit_type);
|
||||||
|
write_slice_header (nal_writer, slice_param, sequence_param, picture_param,
|
||||||
|
nal_ref_idc);
|
||||||
|
|
||||||
|
return end_bitstream (nal_writer, bitstream_length);
|
||||||
|
}
|
||||||
|
|
||||||
|
GrdNalWriter *
|
||||||
|
grd_nal_writer_new (void)
|
||||||
|
{
|
||||||
|
return g_object_new (GRD_TYPE_NAL_WRITER, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
grd_nal_writer_dispose (GObject *object)
|
||||||
|
{
|
||||||
|
GrdNalWriter *nal_writer = GRD_NAL_WRITER (object);
|
||||||
|
|
||||||
|
g_assert (!nal_writer->nal_bitstream);
|
||||||
|
|
||||||
|
G_OBJECT_CLASS (grd_nal_writer_parent_class)->dispose (object);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
grd_nal_writer_init (GrdNalWriter *nal_writer)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
grd_nal_writer_class_init (GrdNalWriterClass *klass)
|
||||||
|
{
|
||||||
|
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
||||||
|
|
||||||
|
object_class->dispose = grd_nal_writer_dispose;
|
||||||
|
}
|
||||||
46
grd-nal-writer.h
Normal file
46
grd-nal-writer.h
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2022 Pascal Nowack
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License as
|
||||||
|
* published by the Free Software Foundation; either version 2 of the
|
||||||
|
* License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
|
||||||
|
* 02111-1307, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <glib-object.h>
|
||||||
|
#include <va/va.h>
|
||||||
|
|
||||||
|
#define GRD_TYPE_NAL_WRITER (grd_nal_writer_get_type ())
|
||||||
|
G_DECLARE_FINAL_TYPE (GrdNalWriter, grd_nal_writer,
|
||||||
|
GRD, NAL_WRITER, GObject)
|
||||||
|
|
||||||
|
GrdNalWriter *grd_nal_writer_new (void);
|
||||||
|
|
||||||
|
uint8_t *grd_nal_writer_get_aud_bitstream (GrdNalWriter *nal_writer,
|
||||||
|
uint32_t *bitstream_length);
|
||||||
|
|
||||||
|
uint8_t *grd_nal_writer_get_sps_bitstream (GrdNalWriter *nal_writer,
|
||||||
|
const VAEncSequenceParameterBufferH264 *sequence_param,
|
||||||
|
uint32_t *bitstream_length);
|
||||||
|
|
||||||
|
uint8_t *grd_nal_writer_get_pps_bitstream (GrdNalWriter *nal_writer,
|
||||||
|
const VAEncPictureParameterBufferH264 *picture_param,
|
||||||
|
uint32_t *bitstream_length);
|
||||||
|
|
||||||
|
uint8_t *grd_nal_writer_get_slice_header_bitstream (GrdNalWriter *nal_writer,
|
||||||
|
const VAEncSliceParameterBufferH264 *slice_param,
|
||||||
|
const VAEncSequenceParameterBufferH264 *sequence_param,
|
||||||
|
const VAEncPictureParameterBufferH264 *picture_param,
|
||||||
|
uint32_t *bitstream_length);
|
||||||
207
grd-pipewire-utils.c
Normal file
207
grd-pipewire-utils.c
Normal file
@@ -0,0 +1,207 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2015 Red Hat Inc.
|
||||||
|
* Copyright (C) 2020 Pascal Nowack
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License as
|
||||||
|
* published by the Free Software Foundation; either version 2 of the
|
||||||
|
* License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
|
||||||
|
* 02111-1307, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
|
#include "grd-pipewire-utils.h"
|
||||||
|
|
||||||
|
#include <drm_fourcc.h>
|
||||||
|
#include <spa/param/video/raw.h>
|
||||||
|
#include <spa/utils/result.h>
|
||||||
|
|
||||||
|
static void
|
||||||
|
pipewire_source_unref (GrdPipeWireSource *pipewire_source);
|
||||||
|
|
||||||
|
G_DEFINE_AUTOPTR_CLEANUP_FUNC (GrdPipeWireSource, pipewire_source_unref)
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
pipewire_loop_source_prepare (GSource *base,
|
||||||
|
int *timeout)
|
||||||
|
{
|
||||||
|
*timeout = -1;
|
||||||
|
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
pipewire_loop_source_dispatch (GSource *source,
|
||||||
|
GSourceFunc callback,
|
||||||
|
gpointer user_data)
|
||||||
|
{
|
||||||
|
GrdPipeWireSource *pipewire_source = (GrdPipeWireSource *) source;
|
||||||
|
int result;
|
||||||
|
|
||||||
|
result = pw_loop_iterate (pipewire_source->pipewire_loop, 0);
|
||||||
|
if (result < 0)
|
||||||
|
{
|
||||||
|
g_warning ("%s pw_loop_iterate() failed: %s", pipewire_source->message_tag,
|
||||||
|
spa_strerror (result));
|
||||||
|
}
|
||||||
|
|
||||||
|
return G_SOURCE_CONTINUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
pipewire_loop_source_finalize (GSource *source)
|
||||||
|
{
|
||||||
|
GrdPipeWireSource *pipewire_source = (GrdPipeWireSource *) source;
|
||||||
|
|
||||||
|
if (pipewire_source->pipewire_loop)
|
||||||
|
{
|
||||||
|
pw_loop_leave (pipewire_source->pipewire_loop);
|
||||||
|
pw_loop_destroy (pipewire_source->pipewire_loop);
|
||||||
|
}
|
||||||
|
g_clear_pointer (&pipewire_source->message_tag, g_free);
|
||||||
|
}
|
||||||
|
|
||||||
|
static GSourceFuncs pipewire_source_funcs =
|
||||||
|
{
|
||||||
|
pipewire_loop_source_prepare,
|
||||||
|
NULL,
|
||||||
|
pipewire_loop_source_dispatch,
|
||||||
|
pipewire_loop_source_finalize,
|
||||||
|
};
|
||||||
|
|
||||||
|
GrdPipeWireSource *
|
||||||
|
grd_pipewire_source_new (const char *message_tag,
|
||||||
|
GError **error)
|
||||||
|
{
|
||||||
|
g_autoptr (GrdPipeWireSource) pipewire_source = NULL;
|
||||||
|
|
||||||
|
g_assert (strlen (message_tag) > 0);
|
||||||
|
|
||||||
|
pipewire_source =
|
||||||
|
(GrdPipeWireSource *) g_source_new (&pipewire_source_funcs,
|
||||||
|
sizeof (GrdPipeWireSource));
|
||||||
|
pipewire_source->message_tag = g_strdup_printf ("[%s]", message_tag);
|
||||||
|
|
||||||
|
pipewire_source->pipewire_loop = pw_loop_new (NULL);
|
||||||
|
if (!pipewire_source->pipewire_loop)
|
||||||
|
{
|
||||||
|
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
|
||||||
|
"Failed to create PipeWire loop");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
g_source_add_unix_fd (&pipewire_source->base,
|
||||||
|
pw_loop_get_fd (pipewire_source->pipewire_loop),
|
||||||
|
G_IO_IN | G_IO_ERR);
|
||||||
|
|
||||||
|
pw_loop_enter (pipewire_source->pipewire_loop);
|
||||||
|
|
||||||
|
return g_steal_pointer (&pipewire_source);
|
||||||
|
}
|
||||||
|
|
||||||
|
GrdPipeWireSource *
|
||||||
|
grd_attached_pipewire_source_new (const char *message_tag,
|
||||||
|
GError **error)
|
||||||
|
{
|
||||||
|
GrdPipeWireSource *pipewire_source;
|
||||||
|
|
||||||
|
pipewire_source = grd_pipewire_source_new (message_tag, error);
|
||||||
|
if (!pipewire_source)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
g_source_attach (&pipewire_source->base, NULL);
|
||||||
|
|
||||||
|
return pipewire_source;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
pipewire_source_unref (GrdPipeWireSource *pipewire_source)
|
||||||
|
{
|
||||||
|
g_source_unref (&pipewire_source->base);
|
||||||
|
}
|
||||||
|
|
||||||
|
gboolean
|
||||||
|
grd_pipewire_buffer_has_pointer_bitmap (struct pw_buffer *buffer)
|
||||||
|
{
|
||||||
|
struct spa_meta_cursor *spa_meta_cursor;
|
||||||
|
|
||||||
|
spa_meta_cursor = spa_buffer_find_meta_data (buffer->buffer, SPA_META_Cursor,
|
||||||
|
sizeof *spa_meta_cursor);
|
||||||
|
if (spa_meta_cursor && spa_meta_cursor_is_valid (spa_meta_cursor))
|
||||||
|
{
|
||||||
|
struct spa_meta_bitmap *spa_meta_bitmap = NULL;
|
||||||
|
|
||||||
|
if (spa_meta_cursor->bitmap_offset)
|
||||||
|
{
|
||||||
|
spa_meta_bitmap = SPA_MEMBER (spa_meta_cursor,
|
||||||
|
spa_meta_cursor->bitmap_offset,
|
||||||
|
struct spa_meta_bitmap);
|
||||||
|
}
|
||||||
|
if (spa_meta_bitmap)
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
gboolean
|
||||||
|
grd_pipewire_buffer_has_frame_data (struct pw_buffer *buffer)
|
||||||
|
{
|
||||||
|
return buffer->buffer->datas[0].chunk->size > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
gboolean
|
||||||
|
grd_spa_pixel_format_to_grd_pixel_format (uint32_t spa_format,
|
||||||
|
GrdPixelFormat *out_format)
|
||||||
|
{
|
||||||
|
if (spa_format == SPA_VIDEO_FORMAT_RGBA)
|
||||||
|
*out_format = GRD_PIXEL_FORMAT_RGBA8888;
|
||||||
|
else
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct
|
||||||
|
{
|
||||||
|
enum spa_video_format spa_format;
|
||||||
|
uint32_t drm_format;
|
||||||
|
int bpp;
|
||||||
|
} format_table[] = {
|
||||||
|
{ SPA_VIDEO_FORMAT_ARGB, DRM_FORMAT_BGRA8888, 4 },
|
||||||
|
{ SPA_VIDEO_FORMAT_BGRA, DRM_FORMAT_ARGB8888, 4 },
|
||||||
|
{ SPA_VIDEO_FORMAT_xRGB, DRM_FORMAT_BGRX8888, 4 },
|
||||||
|
{ SPA_VIDEO_FORMAT_BGRx, DRM_FORMAT_XRGB8888, 4 },
|
||||||
|
};
|
||||||
|
|
||||||
|
void
|
||||||
|
grd_get_spa_format_details (enum spa_video_format spa_format,
|
||||||
|
uint32_t *drm_format,
|
||||||
|
int *bpp)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < G_N_ELEMENTS (format_table); i++)
|
||||||
|
{
|
||||||
|
if (format_table[i].spa_format == spa_format)
|
||||||
|
{
|
||||||
|
if (drm_format)
|
||||||
|
*drm_format = format_table[i].drm_format;
|
||||||
|
if (bpp)
|
||||||
|
*bpp = format_table[i].bpp;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
g_assert_not_reached ();
|
||||||
|
}
|
||||||
56
grd-pipewire-utils.h
Normal file
56
grd-pipewire-utils.h
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2020 Pascal Nowack
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License as
|
||||||
|
* published by the Free Software Foundation; either version 2 of the
|
||||||
|
* License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
|
||||||
|
* 02111-1307, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <gio/gio.h>
|
||||||
|
#include <pipewire/pipewire.h>
|
||||||
|
#include <spa/param/video/format-utils.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#include "grd-types.h"
|
||||||
|
|
||||||
|
#define CURSOR_META_SIZE(width, height) \
|
||||||
|
(sizeof(struct spa_meta_cursor) + \
|
||||||
|
sizeof(struct spa_meta_bitmap) + width * height * 4)
|
||||||
|
|
||||||
|
typedef struct _GrdPipeWireSource
|
||||||
|
{
|
||||||
|
GSource base;
|
||||||
|
|
||||||
|
char *message_tag;
|
||||||
|
struct pw_loop *pipewire_loop;
|
||||||
|
} GrdPipeWireSource;
|
||||||
|
|
||||||
|
GrdPipeWireSource *grd_pipewire_source_new (const char *message_tag,
|
||||||
|
GError **error);
|
||||||
|
|
||||||
|
GrdPipeWireSource *grd_attached_pipewire_source_new (const char *message_tag,
|
||||||
|
GError **error);
|
||||||
|
|
||||||
|
gboolean grd_pipewire_buffer_has_pointer_bitmap (struct pw_buffer *buffer);
|
||||||
|
|
||||||
|
gboolean grd_pipewire_buffer_has_frame_data (struct pw_buffer *buffer);
|
||||||
|
|
||||||
|
gboolean grd_spa_pixel_format_to_grd_pixel_format (uint32_t spa_format,
|
||||||
|
GrdPixelFormat *out_format);
|
||||||
|
|
||||||
|
void grd_get_spa_format_details (enum spa_video_format spa_format,
|
||||||
|
uint32_t *drm_format,
|
||||||
|
int *bpp);
|
||||||
44
grd-private.h
Normal file
44
grd-private.h
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2015 Red Hat Inc.
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License as
|
||||||
|
* published by the Free Software Foundation; either version 2 of the
|
||||||
|
* License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
|
||||||
|
* 02111-1307, USA.
|
||||||
|
*
|
||||||
|
* Written by:
|
||||||
|
* Jonas Ådahl <jadahl@gmail.com>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#define GRD_DAEMON_USER_APPLICATION_ID "org.gnome.RemoteDesktop.User"
|
||||||
|
#define GRD_DAEMON_HEADLESS_APPLICATION_ID "org.gnome.RemoteDesktop.Headless"
|
||||||
|
#define GRD_DAEMON_SYSTEM_APPLICATION_ID "org.gnome.RemoteDesktop"
|
||||||
|
#define GRD_DAEMON_HANDOVER_APPLICATION_ID "org.gnome.RemoteDesktop.Handover"
|
||||||
|
#define REMOTE_DESKTOP_BUS_NAME "org.gnome.RemoteDesktop"
|
||||||
|
#define REMOTE_DESKTOP_OBJECT_PATH "/org/gnome/RemoteDesktop"
|
||||||
|
#define REMOTE_DESKTOP_CONFIGURATION_BUS_NAME "org.gnome.RemoteDesktop.Configuration"
|
||||||
|
#define REMOTE_DESKTOP_CONFIGURATION_OBJECT_PATH "/org/gnome/RemoteDesktop/Configuration"
|
||||||
|
#define REMOTE_DESKTOP_CLIENT_OBJECT_PATH "/org/gnome/RemoteDesktop/Client"
|
||||||
|
#define REMOTE_DESKTOP_DISPATCHER_OBJECT_PATH "/org/gnome/RemoteDesktop/Rdp/Dispatcher"
|
||||||
|
#define REMOTE_DESKTOP_HANDOVERS_OBJECT_PATH "/org/gnome/RemoteDesktop/Rdp/Handovers"
|
||||||
|
#define GRD_RDP_SERVER_OBJECT_PATH "/org/gnome/RemoteDesktop/Rdp/Server"
|
||||||
|
#define GRD_VNC_SERVER_OBJECT_PATH "/org/gnome/RemoteDesktop/Vnc/Server"
|
||||||
|
#define MUTTER_REMOTE_DESKTOP_BUS_NAME "org.gnome.Mutter.RemoteDesktop"
|
||||||
|
#define MUTTER_REMOTE_DESKTOP_OBJECT_PATH "/org/gnome/Mutter/RemoteDesktop"
|
||||||
|
#define MUTTER_SCREEN_CAST_BUS_NAME "org.gnome.Mutter.ScreenCast"
|
||||||
|
#define MUTTER_SCREEN_CAST_OBJECT_PATH "/org/gnome/Mutter/ScreenCast"
|
||||||
|
#define GDM_BUS_NAME "org.gnome.DisplayManager"
|
||||||
|
#define GDM_REMOTE_DISPLAY_FACTORY_OBJECT_PATH "/org/gnome/DisplayManager/RemoteDisplayFactory"
|
||||||
|
#define GDM_OBJECT_MANAGER_OBJECT_PATH "/org/gnome/DisplayManager/Displays"
|
||||||
207
grd-prompt.c
Normal file
207
grd-prompt.c
Normal file
@@ -0,0 +1,207 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2018 Red Hat Inc.
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License as
|
||||||
|
* published by the Free Software Foundation; either version 2 of the
|
||||||
|
* License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
|
||||||
|
* 02111-1307, USA.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
|
#include <libnotify/notify.h>
|
||||||
|
|
||||||
|
#include "grd-prompt.h"
|
||||||
|
|
||||||
|
typedef struct _GrdPromptResult
|
||||||
|
{
|
||||||
|
GrdPromptResponse response;
|
||||||
|
} GrdPromptResult;
|
||||||
|
|
||||||
|
struct _GrdPrompt
|
||||||
|
{
|
||||||
|
GObject parent;
|
||||||
|
};
|
||||||
|
|
||||||
|
G_DEFINE_TYPE (GrdPrompt, grd_prompt, G_TYPE_OBJECT)
|
||||||
|
|
||||||
|
static void
|
||||||
|
handle_notification_response (NotifyNotification *notification,
|
||||||
|
char *response,
|
||||||
|
gpointer user_data)
|
||||||
|
{
|
||||||
|
GTask *task = G_TASK (user_data);
|
||||||
|
|
||||||
|
if (g_strcmp0 (response, "accept") == 0)
|
||||||
|
{
|
||||||
|
g_task_return_int (task, GRD_PROMPT_RESPONSE_ACCEPT);
|
||||||
|
}
|
||||||
|
else if (g_strcmp0 (response, "cancel") == 0 ||
|
||||||
|
g_strcmp0 (response, "closed") == 0)
|
||||||
|
{
|
||||||
|
g_task_return_int (task, GRD_PROMPT_RESPONSE_CANCEL);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
g_warning ("Unknown prompt response '%s'", response);
|
||||||
|
g_task_return_int (task, GRD_PROMPT_RESPONSE_CANCEL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
on_notification_closed (NotifyNotification *notification,
|
||||||
|
gpointer user_data)
|
||||||
|
{
|
||||||
|
handle_notification_response (notification, "closed", user_data);
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
cancelled_idle_callback (gpointer user_data)
|
||||||
|
{
|
||||||
|
GTask *task = G_TASK (user_data);
|
||||||
|
|
||||||
|
g_task_return_new_error (task, G_IO_ERROR, G_IO_ERROR_CANCELLED,
|
||||||
|
"Prompt was cancelled");
|
||||||
|
return G_SOURCE_REMOVE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
on_cancellable_cancelled (GCancellable *cancellable,
|
||||||
|
GTask *task)
|
||||||
|
{
|
||||||
|
NotifyNotification *notification =
|
||||||
|
NOTIFY_NOTIFICATION (g_task_get_task_data (task));
|
||||||
|
|
||||||
|
g_signal_handlers_disconnect_by_func (notification,
|
||||||
|
G_CALLBACK (on_notification_closed),
|
||||||
|
task);
|
||||||
|
notify_notification_close (notification, NULL);
|
||||||
|
|
||||||
|
g_idle_add (cancelled_idle_callback, task);
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
show_notification_idle_callback (gpointer user_data)
|
||||||
|
{
|
||||||
|
GTask *task = G_TASK (user_data);
|
||||||
|
GCancellable *cancellable = g_task_get_cancellable (task);
|
||||||
|
NotifyNotification *notification;
|
||||||
|
GError *error = NULL;
|
||||||
|
|
||||||
|
if (g_cancellable_is_cancelled (cancellable))
|
||||||
|
return G_SOURCE_REMOVE;
|
||||||
|
|
||||||
|
notification = g_task_get_task_data (task);
|
||||||
|
|
||||||
|
if (!notify_notification_show (notification, &error))
|
||||||
|
g_task_return_error (task, error);
|
||||||
|
|
||||||
|
return G_SOURCE_REMOVE;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
grd_prompt_query_async (GrdPrompt *prompt,
|
||||||
|
GrdPromptDefinition *prompt_definition,
|
||||||
|
GCancellable *cancellable,
|
||||||
|
GAsyncReadyCallback callback,
|
||||||
|
gpointer user_data)
|
||||||
|
{
|
||||||
|
NotifyNotification *notification;
|
||||||
|
GTask *task;
|
||||||
|
|
||||||
|
task = g_task_new (G_OBJECT (prompt), cancellable, callback, user_data);
|
||||||
|
|
||||||
|
g_assert (prompt_definition);
|
||||||
|
g_assert (prompt_definition->summary ||
|
||||||
|
prompt_definition->body);
|
||||||
|
|
||||||
|
notification = notify_notification_new (prompt_definition->summary,
|
||||||
|
prompt_definition->body,
|
||||||
|
"preferences-desktop-remote-desktop");
|
||||||
|
|
||||||
|
notify_notification_set_urgency (notification, NOTIFY_URGENCY_CRITICAL);
|
||||||
|
|
||||||
|
if (prompt_definition->cancel_label)
|
||||||
|
{
|
||||||
|
notify_notification_add_action (notification,
|
||||||
|
"cancel",
|
||||||
|
prompt_definition->cancel_label,
|
||||||
|
handle_notification_response,
|
||||||
|
task, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (prompt_definition->accept_label)
|
||||||
|
{
|
||||||
|
notify_notification_add_action (notification,
|
||||||
|
"accept",
|
||||||
|
prompt_definition->accept_label,
|
||||||
|
handle_notification_response,
|
||||||
|
task, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
g_task_set_task_data (task, notification, g_object_unref);
|
||||||
|
|
||||||
|
g_signal_connect (notification, "closed",
|
||||||
|
G_CALLBACK (on_notification_closed), task);
|
||||||
|
|
||||||
|
g_cancellable_connect (cancellable,
|
||||||
|
G_CALLBACK (on_cancellable_cancelled),
|
||||||
|
task, NULL);
|
||||||
|
|
||||||
|
g_idle_add (show_notification_idle_callback, task);
|
||||||
|
}
|
||||||
|
|
||||||
|
gboolean
|
||||||
|
grd_prompt_query_finish (GrdPrompt *prompt,
|
||||||
|
GAsyncResult *result,
|
||||||
|
GrdPromptResponse *out_response,
|
||||||
|
GError **error)
|
||||||
|
{
|
||||||
|
g_autoptr(GTask) task = G_TASK (result);
|
||||||
|
GCancellable *cancellable;
|
||||||
|
GrdPromptResponse response;
|
||||||
|
|
||||||
|
cancellable = g_task_get_cancellable (task);
|
||||||
|
g_signal_handlers_disconnect_by_func (cancellable,
|
||||||
|
G_CALLBACK (on_cancellable_cancelled),
|
||||||
|
task);
|
||||||
|
|
||||||
|
response = g_task_propagate_int (task, error);
|
||||||
|
if (response == -1)
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
*out_response = response;
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
grd_prompt_definition_free (GrdPromptDefinition *prompt_definition)
|
||||||
|
{
|
||||||
|
g_clear_pointer (&prompt_definition->summary, g_free);
|
||||||
|
g_clear_pointer (&prompt_definition->body, g_free);
|
||||||
|
g_clear_pointer (&prompt_definition->accept_label, g_free);
|
||||||
|
g_clear_pointer (&prompt_definition->cancel_label, g_free);
|
||||||
|
g_free (prompt_definition);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
grd_prompt_init (GrdPrompt *prompt)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
grd_prompt_class_init (GrdPromptClass *klass)
|
||||||
|
{
|
||||||
|
notify_init (g_get_application_name ());
|
||||||
|
}
|
||||||
53
grd-prompt.h
Normal file
53
grd-prompt.h
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2018 Red Hat Inc.
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License as
|
||||||
|
* published by the Free Software Foundation; either version 2 of the
|
||||||
|
* License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
|
||||||
|
* 02111-1307, USA.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
typedef enum _GrdPromptResponse
|
||||||
|
{
|
||||||
|
GRD_PROMPT_RESPONSE_ACCEPT,
|
||||||
|
GRD_PROMPT_RESPONSE_CANCEL
|
||||||
|
} GrdPromptResponse;
|
||||||
|
|
||||||
|
typedef struct _GrdPromptDefinition
|
||||||
|
{
|
||||||
|
char *summary;
|
||||||
|
char *body;
|
||||||
|
char *accept_label;
|
||||||
|
char *cancel_label;
|
||||||
|
} GrdPromptDefinition;
|
||||||
|
|
||||||
|
#define GRD_TYPE_PROMPT (grd_prompt_get_type ())
|
||||||
|
G_DECLARE_FINAL_TYPE (GrdPrompt, grd_prompt, GRD, PROMPT, GObject)
|
||||||
|
|
||||||
|
void grd_prompt_query_async (GrdPrompt *prompt,
|
||||||
|
GrdPromptDefinition *prompt_definition,
|
||||||
|
GCancellable *cancellable,
|
||||||
|
GAsyncReadyCallback callback,
|
||||||
|
gpointer user_data);
|
||||||
|
|
||||||
|
gboolean grd_prompt_query_finish (GrdPrompt *prompt,
|
||||||
|
GAsyncResult *result,
|
||||||
|
GrdPromptResponse *response,
|
||||||
|
GError **error);
|
||||||
|
|
||||||
|
void grd_prompt_definition_free (GrdPromptDefinition *prompt_definition);
|
||||||
|
|
||||||
|
G_DEFINE_AUTOPTR_CLEANUP_FUNC (GrdPromptDefinition, grd_prompt_definition_free)
|
||||||
387
grd-rdp-audio-output-stream.c
Normal file
387
grd-rdp-audio-output-stream.c
Normal file
@@ -0,0 +1,387 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2022 Pascal Nowack
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License as
|
||||||
|
* published by the Free Software Foundation; either version 2 of the
|
||||||
|
* License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
|
||||||
|
* 02111-1307, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
|
#include "grd-rdp-audio-output-stream.h"
|
||||||
|
|
||||||
|
#include <gio/gio.h>
|
||||||
|
#include <spa/param/audio/format-utils.h>
|
||||||
|
#include <spa/param/props.h>
|
||||||
|
|
||||||
|
#include "grd-rdp-dvc-audio-playback.h"
|
||||||
|
|
||||||
|
struct _GrdRdpAudioOutputStream
|
||||||
|
{
|
||||||
|
GObject parent;
|
||||||
|
|
||||||
|
uint32_t target_node_id;
|
||||||
|
|
||||||
|
GrdRdpDvcAudioPlayback *audio_playback;
|
||||||
|
|
||||||
|
struct pw_node *pipewire_node;
|
||||||
|
struct spa_hook pipewire_node_listener;
|
||||||
|
|
||||||
|
struct pw_stream *pipewire_stream;
|
||||||
|
struct spa_hook pipewire_stream_listener;
|
||||||
|
|
||||||
|
GMutex volume_mutex;
|
||||||
|
gboolean audio_muted;
|
||||||
|
float volumes[SPA_AUDIO_MAX_CHANNELS];
|
||||||
|
uint32_t n_volumes;
|
||||||
|
};
|
||||||
|
|
||||||
|
G_DEFINE_TYPE (GrdRdpAudioOutputStream, grd_rdp_audio_output_stream, G_TYPE_OBJECT)
|
||||||
|
|
||||||
|
void
|
||||||
|
grd_rdp_audio_output_stream_set_active (GrdRdpAudioOutputStream *audio_output_stream,
|
||||||
|
gboolean active)
|
||||||
|
{
|
||||||
|
pw_stream_set_active (audio_output_stream->pipewire_stream, active);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
grd_rdp_audio_output_stream_get_volume_data (GrdRdpAudioOutputStream *audio_output_stream,
|
||||||
|
GrdRdpAudioVolumeData *volume_data)
|
||||||
|
{
|
||||||
|
uint32_t i;
|
||||||
|
|
||||||
|
g_mutex_lock (&audio_output_stream->volume_mutex);
|
||||||
|
volume_data->audio_muted = audio_output_stream->audio_muted;
|
||||||
|
volume_data->n_volumes = audio_output_stream->n_volumes;
|
||||||
|
|
||||||
|
for (i = 0; i < audio_output_stream->n_volumes; ++i)
|
||||||
|
volume_data->volumes[i] = audio_output_stream->volumes[i];
|
||||||
|
g_mutex_unlock (&audio_output_stream->volume_mutex);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
pipewire_node_info (void *user_data,
|
||||||
|
const struct pw_node_info *info)
|
||||||
|
{
|
||||||
|
GrdRdpAudioOutputStream *audio_output_stream = user_data;
|
||||||
|
uint32_t i;
|
||||||
|
|
||||||
|
if (!(info->change_mask & PW_NODE_CHANGE_MASK_PARAMS))
|
||||||
|
return;
|
||||||
|
|
||||||
|
for (i = 0; i < info->n_params; ++i)
|
||||||
|
{
|
||||||
|
if (info->params[i].id == SPA_PARAM_Props &&
|
||||||
|
info->params[i].flags & SPA_PARAM_INFO_READ)
|
||||||
|
{
|
||||||
|
pw_node_enum_params (audio_output_stream->pipewire_node,
|
||||||
|
0, SPA_PARAM_Props, 0, -1, NULL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
pipewire_node_param (void *user_data,
|
||||||
|
int seq,
|
||||||
|
uint32_t id,
|
||||||
|
uint32_t index,
|
||||||
|
uint32_t next,
|
||||||
|
const struct spa_pod *param)
|
||||||
|
{
|
||||||
|
GrdRdpAudioOutputStream *audio_output_stream = user_data;
|
||||||
|
const struct spa_pod_object *object = (const struct spa_pod_object *) param;
|
||||||
|
struct spa_pod_prop *prop;
|
||||||
|
|
||||||
|
if (id != SPA_PARAM_Props)
|
||||||
|
return;
|
||||||
|
|
||||||
|
SPA_POD_OBJECT_FOREACH (object, prop)
|
||||||
|
{
|
||||||
|
if (prop->key == SPA_PROP_mute)
|
||||||
|
{
|
||||||
|
bool mute;
|
||||||
|
|
||||||
|
if (spa_pod_get_bool (&prop->value, &mute) < 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
g_mutex_lock (&audio_output_stream->volume_mutex);
|
||||||
|
audio_output_stream->audio_muted = !!mute;
|
||||||
|
g_mutex_unlock (&audio_output_stream->volume_mutex);
|
||||||
|
}
|
||||||
|
else if (prop->key == SPA_PROP_channelVolumes)
|
||||||
|
{
|
||||||
|
g_mutex_lock (&audio_output_stream->volume_mutex);
|
||||||
|
audio_output_stream->n_volumes =
|
||||||
|
spa_pod_copy_array (&prop->value, SPA_TYPE_Float,
|
||||||
|
audio_output_stream->volumes,
|
||||||
|
SPA_AUDIO_MAX_CHANNELS);
|
||||||
|
g_mutex_unlock (&audio_output_stream->volume_mutex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct pw_node_events pipewire_node_events =
|
||||||
|
{
|
||||||
|
.version = PW_VERSION_NODE_EVENTS,
|
||||||
|
.info = pipewire_node_info,
|
||||||
|
.param = pipewire_node_param,
|
||||||
|
};
|
||||||
|
|
||||||
|
static void
|
||||||
|
pipewire_stream_state_changed (void *user_data,
|
||||||
|
enum pw_stream_state old,
|
||||||
|
enum pw_stream_state state,
|
||||||
|
const char *error)
|
||||||
|
{
|
||||||
|
GrdRdpAudioOutputStream *audio_output_stream = user_data;
|
||||||
|
|
||||||
|
g_debug ("[RDP.AUDIO_PLAYBACK] Node %u: PipeWire stream state changed from "
|
||||||
|
"%s to %s", audio_output_stream->target_node_id,
|
||||||
|
pw_stream_state_as_string (old),
|
||||||
|
pw_stream_state_as_string (state));
|
||||||
|
|
||||||
|
switch (state)
|
||||||
|
{
|
||||||
|
case PW_STREAM_STATE_ERROR:
|
||||||
|
g_warning ("[RDP.AUDIO_PLAYBACK] Node %u: PipeWire stream error: %s",
|
||||||
|
audio_output_stream->target_node_id, error);
|
||||||
|
break;
|
||||||
|
case PW_STREAM_STATE_PAUSED:
|
||||||
|
case PW_STREAM_STATE_STREAMING:
|
||||||
|
case PW_STREAM_STATE_UNCONNECTED:
|
||||||
|
case PW_STREAM_STATE_CONNECTING:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
pipewire_stream_param_changed (void *user_data,
|
||||||
|
uint32_t id,
|
||||||
|
const struct spa_pod *param)
|
||||||
|
{
|
||||||
|
GrdRdpAudioOutputStream *audio_output_stream = user_data;
|
||||||
|
struct spa_pod_builder pod_builder;
|
||||||
|
const struct spa_pod *params[1];
|
||||||
|
uint8_t params_buffer[1024];
|
||||||
|
|
||||||
|
if (!param || id != SPA_PARAM_Format)
|
||||||
|
return;
|
||||||
|
|
||||||
|
pod_builder = SPA_POD_BUILDER_INIT (params_buffer, sizeof (params_buffer));
|
||||||
|
|
||||||
|
params[0] = spa_pod_builder_add_object (
|
||||||
|
&pod_builder,
|
||||||
|
SPA_TYPE_OBJECT_ParamBuffers, SPA_PARAM_Buffers,
|
||||||
|
SPA_PARAM_BUFFERS_dataType, SPA_POD_Int (1 << SPA_DATA_MemPtr));
|
||||||
|
|
||||||
|
pw_stream_update_params (audio_output_stream->pipewire_stream, params, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
pipewire_stream_process (void *user_data)
|
||||||
|
{
|
||||||
|
GrdRdpAudioOutputStream *audio_output_stream = user_data;
|
||||||
|
struct pw_buffer *next_buffer;
|
||||||
|
struct pw_buffer *buffer = NULL;
|
||||||
|
int16_t *data;
|
||||||
|
uint32_t offset;
|
||||||
|
uint32_t size;
|
||||||
|
|
||||||
|
next_buffer = pw_stream_dequeue_buffer (audio_output_stream->pipewire_stream);
|
||||||
|
while (next_buffer)
|
||||||
|
{
|
||||||
|
buffer = next_buffer;
|
||||||
|
next_buffer = pw_stream_dequeue_buffer (audio_output_stream->pipewire_stream);
|
||||||
|
|
||||||
|
if (next_buffer)
|
||||||
|
pw_stream_queue_buffer (audio_output_stream->pipewire_stream, buffer);
|
||||||
|
}
|
||||||
|
if (!buffer)
|
||||||
|
return;
|
||||||
|
|
||||||
|
offset = buffer->buffer->datas[0].chunk->offset;
|
||||||
|
size = buffer->buffer->datas[0].chunk->size;
|
||||||
|
data = buffer->buffer->datas[0].data + offset;
|
||||||
|
|
||||||
|
if (size > 0)
|
||||||
|
{
|
||||||
|
GrdRdpDvcAudioPlayback *audio_playback =
|
||||||
|
audio_output_stream->audio_playback;
|
||||||
|
uint32_t node_id = audio_output_stream->target_node_id;
|
||||||
|
GrdRdpAudioVolumeData volume_data = {};
|
||||||
|
|
||||||
|
grd_rdp_audio_output_stream_get_volume_data (audio_output_stream,
|
||||||
|
&volume_data);
|
||||||
|
grd_rdp_dvc_audio_playback_maybe_submit_samples (audio_playback, node_id,
|
||||||
|
&volume_data, data, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
pw_stream_queue_buffer (audio_output_stream->pipewire_stream, buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct pw_stream_events pipewire_stream_events =
|
||||||
|
{
|
||||||
|
.version = PW_VERSION_STREAM_EVENTS,
|
||||||
|
.state_changed = pipewire_stream_state_changed,
|
||||||
|
.param_changed = pipewire_stream_param_changed,
|
||||||
|
.process = pipewire_stream_process,
|
||||||
|
};
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
connect_to_stream (GrdRdpAudioOutputStream *audio_output_stream,
|
||||||
|
struct pw_core *pipewire_core,
|
||||||
|
uint32_t target_node_id,
|
||||||
|
uint32_t n_samples_per_sec,
|
||||||
|
uint32_t n_channels,
|
||||||
|
uint32_t *position,
|
||||||
|
GError **error)
|
||||||
|
{
|
||||||
|
struct spa_pod_builder pod_builder;
|
||||||
|
const struct spa_pod *params[1];
|
||||||
|
uint8_t params_buffer[1024];
|
||||||
|
int result;
|
||||||
|
|
||||||
|
pod_builder = SPA_POD_BUILDER_INIT (params_buffer, sizeof (params_buffer));
|
||||||
|
params[0] = spa_pod_builder_add_object (
|
||||||
|
&pod_builder,
|
||||||
|
SPA_TYPE_OBJECT_Format, SPA_PARAM_EnumFormat,
|
||||||
|
SPA_FORMAT_mediaType, SPA_POD_Id (SPA_MEDIA_TYPE_audio),
|
||||||
|
SPA_FORMAT_mediaSubtype, SPA_POD_Id (SPA_MEDIA_SUBTYPE_raw),
|
||||||
|
SPA_FORMAT_AUDIO_format, SPA_POD_Id (SPA_AUDIO_FORMAT_S16),
|
||||||
|
SPA_FORMAT_AUDIO_rate, SPA_POD_Int (n_samples_per_sec),
|
||||||
|
SPA_FORMAT_AUDIO_channels, SPA_POD_Int (n_channels),
|
||||||
|
SPA_FORMAT_AUDIO_position, SPA_POD_Array (sizeof (uint32_t), SPA_TYPE_Id,
|
||||||
|
n_channels, position),
|
||||||
|
0);
|
||||||
|
|
||||||
|
audio_output_stream->pipewire_stream =
|
||||||
|
pw_stream_new (pipewire_core,
|
||||||
|
"GRD::RDP::AUDIO_PLAYBACK",
|
||||||
|
pw_properties_new (PW_KEY_MEDIA_TYPE, "Audio",
|
||||||
|
PW_KEY_MEDIA_CATEGORY, "Capture",
|
||||||
|
PW_KEY_NODE_FORCE_QUANTUM, "256",
|
||||||
|
NULL));
|
||||||
|
if (!audio_output_stream->pipewire_stream)
|
||||||
|
{
|
||||||
|
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
|
||||||
|
"Failed to create PipeWire stream");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
pw_stream_add_listener (audio_output_stream->pipewire_stream,
|
||||||
|
&audio_output_stream->pipewire_stream_listener,
|
||||||
|
&pipewire_stream_events, audio_output_stream);
|
||||||
|
|
||||||
|
result = pw_stream_connect (audio_output_stream->pipewire_stream,
|
||||||
|
PW_DIRECTION_INPUT, target_node_id,
|
||||||
|
PW_STREAM_FLAG_RT_PROCESS |
|
||||||
|
PW_STREAM_FLAG_AUTOCONNECT |
|
||||||
|
PW_STREAM_FLAG_INACTIVE |
|
||||||
|
PW_STREAM_FLAG_MAP_BUFFERS,
|
||||||
|
params, 1);
|
||||||
|
if (result < 0)
|
||||||
|
{
|
||||||
|
g_set_error_literal (error, G_IO_ERROR, g_io_error_from_errno (-result),
|
||||||
|
strerror (-result));
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
GrdRdpAudioOutputStream *
|
||||||
|
grd_rdp_audio_output_stream_new (GrdRdpDvcAudioPlayback *audio_playback,
|
||||||
|
struct pw_core *pipewire_core,
|
||||||
|
struct pw_registry *pipewire_registry,
|
||||||
|
uint32_t target_node_id,
|
||||||
|
uint32_t n_samples_per_sec,
|
||||||
|
uint32_t n_channels,
|
||||||
|
uint32_t *position,
|
||||||
|
GError **error)
|
||||||
|
{
|
||||||
|
g_autoptr (GrdRdpAudioOutputStream) audio_output_stream = NULL;
|
||||||
|
|
||||||
|
audio_output_stream = g_object_new (GRD_TYPE_RDP_AUDIO_OUTPUT_STREAM, NULL);
|
||||||
|
audio_output_stream->audio_playback = audio_playback;
|
||||||
|
audio_output_stream->target_node_id = target_node_id;
|
||||||
|
|
||||||
|
audio_output_stream->pipewire_node = pw_registry_bind (pipewire_registry,
|
||||||
|
target_node_id,
|
||||||
|
PW_TYPE_INTERFACE_Node,
|
||||||
|
PW_VERSION_NODE, 0);
|
||||||
|
if (!audio_output_stream->pipewire_node)
|
||||||
|
{
|
||||||
|
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
|
||||||
|
"Failed to bind PipeWire node");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
pw_node_add_listener (audio_output_stream->pipewire_node,
|
||||||
|
&audio_output_stream->pipewire_node_listener,
|
||||||
|
&pipewire_node_events, audio_output_stream);
|
||||||
|
|
||||||
|
if (!connect_to_stream (audio_output_stream, pipewire_core, target_node_id,
|
||||||
|
n_samples_per_sec, n_channels, position, error))
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
return g_steal_pointer (&audio_output_stream);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
grd_rdp_audio_output_stream_dispose (GObject *object)
|
||||||
|
{
|
||||||
|
GrdRdpAudioOutputStream *audio_output_stream =
|
||||||
|
GRD_RDP_AUDIO_OUTPUT_STREAM (object);
|
||||||
|
|
||||||
|
if (audio_output_stream->pipewire_stream)
|
||||||
|
{
|
||||||
|
pw_stream_destroy (audio_output_stream->pipewire_stream);
|
||||||
|
audio_output_stream->pipewire_stream = NULL;
|
||||||
|
}
|
||||||
|
if (audio_output_stream->pipewire_node)
|
||||||
|
{
|
||||||
|
spa_hook_remove (&audio_output_stream->pipewire_node_listener);
|
||||||
|
pw_proxy_destroy ((struct pw_proxy *) audio_output_stream->pipewire_node);
|
||||||
|
audio_output_stream->pipewire_node = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
G_OBJECT_CLASS (grd_rdp_audio_output_stream_parent_class)->dispose (object);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
grd_rdp_audio_output_stream_finalize (GObject *object)
|
||||||
|
{
|
||||||
|
GrdRdpAudioOutputStream *audio_output_stream =
|
||||||
|
GRD_RDP_AUDIO_OUTPUT_STREAM (object);
|
||||||
|
|
||||||
|
g_mutex_clear (&audio_output_stream->volume_mutex);
|
||||||
|
|
||||||
|
G_OBJECT_CLASS (grd_rdp_audio_output_stream_parent_class)->finalize (object);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
grd_rdp_audio_output_stream_init (GrdRdpAudioOutputStream *audio_output_stream)
|
||||||
|
{
|
||||||
|
g_mutex_init (&audio_output_stream->volume_mutex);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
grd_rdp_audio_output_stream_class_init (GrdRdpAudioOutputStreamClass *klass)
|
||||||
|
{
|
||||||
|
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
||||||
|
|
||||||
|
object_class->dispose = grd_rdp_audio_output_stream_dispose;
|
||||||
|
object_class->finalize = grd_rdp_audio_output_stream_finalize;
|
||||||
|
}
|
||||||
53
grd-rdp-audio-output-stream.h
Normal file
53
grd-rdp-audio-output-stream.h
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2022 Pascal Nowack
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License as
|
||||||
|
* published by the Free Software Foundation; either version 2 of the
|
||||||
|
* License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
|
||||||
|
* 02111-1307, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <glib-object.h>
|
||||||
|
#include <spa/param/audio/raw.h>
|
||||||
|
#include <pipewire/pipewire.h>
|
||||||
|
|
||||||
|
#include "grd-types.h"
|
||||||
|
|
||||||
|
#define GRD_TYPE_RDP_AUDIO_OUTPUT_STREAM (grd_rdp_audio_output_stream_get_type ())
|
||||||
|
G_DECLARE_FINAL_TYPE (GrdRdpAudioOutputStream, grd_rdp_audio_output_stream,
|
||||||
|
GRD, RDP_AUDIO_OUTPUT_STREAM, GObject)
|
||||||
|
|
||||||
|
struct _GrdRdpAudioVolumeData
|
||||||
|
{
|
||||||
|
gboolean audio_muted;
|
||||||
|
|
||||||
|
float volumes[SPA_AUDIO_MAX_CHANNELS];
|
||||||
|
uint32_t n_volumes;
|
||||||
|
};
|
||||||
|
|
||||||
|
GrdRdpAudioOutputStream *grd_rdp_audio_output_stream_new (GrdRdpDvcAudioPlayback *audio_playback,
|
||||||
|
struct pw_core *pipewire_core,
|
||||||
|
struct pw_registry *pipewire_registry,
|
||||||
|
uint32_t target_node_id,
|
||||||
|
uint32_t n_samples_per_sec,
|
||||||
|
uint32_t n_channels,
|
||||||
|
uint32_t *position,
|
||||||
|
GError **error);
|
||||||
|
|
||||||
|
void grd_rdp_audio_output_stream_set_active (GrdRdpAudioOutputStream *audio_output_stream,
|
||||||
|
gboolean active);
|
||||||
|
|
||||||
|
void grd_rdp_audio_output_stream_get_volume_data (GrdRdpAudioOutputStream *audio_output_stream,
|
||||||
|
GrdRdpAudioVolumeData *volume_data);
|
||||||
38
grd-rdp-buffer-info.h
Normal file
38
grd-rdp-buffer-info.h
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2024 Pascal Nowack
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License as
|
||||||
|
* published by the Free Software Foundation; either version 2 of the
|
||||||
|
* License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
|
||||||
|
* 02111-1307, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
typedef enum
|
||||||
|
{
|
||||||
|
GRD_RDP_BUFFER_TYPE_NONE,
|
||||||
|
GRD_RDP_BUFFER_TYPE_DMA_BUF,
|
||||||
|
GRD_RDP_BUFFER_TYPE_MEM_FD,
|
||||||
|
} GrdRdpBufferType;
|
||||||
|
|
||||||
|
struct _GrdRdpBufferInfo
|
||||||
|
{
|
||||||
|
GrdRdpBufferType buffer_type;
|
||||||
|
|
||||||
|
uint32_t drm_format;
|
||||||
|
uint64_t drm_format_modifier;
|
||||||
|
gboolean has_vk_image;
|
||||||
|
};
|
||||||
394
grd-rdp-buffer-pool.c
Normal file
394
grd-rdp-buffer-pool.c
Normal file
@@ -0,0 +1,394 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2022 Pascal Nowack
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License as
|
||||||
|
* published by the Free Software Foundation; either version 2 of the
|
||||||
|
* License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
|
||||||
|
* 02111-1307, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
|
#include "grd-rdp-buffer-pool.h"
|
||||||
|
|
||||||
|
#include "grd-context.h"
|
||||||
|
#include "grd-egl-thread.h"
|
||||||
|
#include "grd-rdp-legacy-buffer.h"
|
||||||
|
#include "grd-rdp-renderer.h"
|
||||||
|
#include "grd-rdp-server.h"
|
||||||
|
#include "grd-rdp-surface.h"
|
||||||
|
#include "grd-session-rdp.h"
|
||||||
|
#include "grd-utils.h"
|
||||||
|
|
||||||
|
typedef struct _BufferInfo
|
||||||
|
{
|
||||||
|
gboolean buffer_taken;
|
||||||
|
} BufferInfo;
|
||||||
|
|
||||||
|
struct _GrdRdpBufferPool
|
||||||
|
{
|
||||||
|
GObject parent;
|
||||||
|
|
||||||
|
GrdRdpSurface *rdp_surface;
|
||||||
|
|
||||||
|
uint32_t buffer_height;
|
||||||
|
uint32_t buffer_stride;
|
||||||
|
|
||||||
|
GSource *resize_pool_source;
|
||||||
|
uint32_t minimum_pool_size;
|
||||||
|
|
||||||
|
GSource *unmap_source;
|
||||||
|
|
||||||
|
GMutex pool_mutex;
|
||||||
|
GHashTable *buffer_table;
|
||||||
|
uint32_t buffers_taken;
|
||||||
|
};
|
||||||
|
|
||||||
|
G_DEFINE_TYPE (GrdRdpBufferPool, grd_rdp_buffer_pool, G_TYPE_OBJECT)
|
||||||
|
|
||||||
|
GrdRdpSurface *
|
||||||
|
grd_rdp_buffer_pool_get_surface (GrdRdpBufferPool *buffer_pool)
|
||||||
|
{
|
||||||
|
return buffer_pool->rdp_surface;
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
add_buffer_to_pool (GrdRdpBufferPool *buffer_pool,
|
||||||
|
gboolean preallocate_on_gpu)
|
||||||
|
{
|
||||||
|
GrdRdpLegacyBuffer *buffer;
|
||||||
|
BufferInfo *buffer_info;
|
||||||
|
|
||||||
|
buffer = grd_rdp_legacy_buffer_new (buffer_pool,
|
||||||
|
buffer_pool->buffer_height,
|
||||||
|
buffer_pool->buffer_stride,
|
||||||
|
preallocate_on_gpu);
|
||||||
|
if (!buffer)
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
buffer_info = g_new0 (BufferInfo, 1);
|
||||||
|
|
||||||
|
g_hash_table_insert (buffer_pool->buffer_table, buffer, buffer_info);
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
fill_buffer_pool (GrdRdpBufferPool *buffer_pool)
|
||||||
|
{
|
||||||
|
uint32_t minimum_size = buffer_pool->minimum_pool_size;
|
||||||
|
|
||||||
|
while (g_hash_table_size (buffer_pool->buffer_table) < minimum_size)
|
||||||
|
{
|
||||||
|
if (!add_buffer_to_pool (buffer_pool, TRUE))
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
gboolean
|
||||||
|
grd_rdp_buffer_pool_resize_buffers (GrdRdpBufferPool *buffer_pool,
|
||||||
|
uint32_t buffer_height,
|
||||||
|
uint32_t buffer_stride)
|
||||||
|
{
|
||||||
|
g_autoptr (GMutexLocker) locker = NULL;
|
||||||
|
|
||||||
|
locker = g_mutex_locker_new (&buffer_pool->pool_mutex);
|
||||||
|
g_assert (buffer_pool->buffers_taken == 0);
|
||||||
|
|
||||||
|
buffer_pool->buffer_height = buffer_height;
|
||||||
|
buffer_pool->buffer_stride = buffer_stride;
|
||||||
|
|
||||||
|
g_hash_table_remove_all (buffer_pool->buffer_table);
|
||||||
|
if (!fill_buffer_pool (buffer_pool))
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
buffer_has_mapped_data (GrdRdpLegacyBuffer *buffer)
|
||||||
|
{
|
||||||
|
if (grd_rdp_legacy_buffer_get_mapped_cuda_pointer (buffer))
|
||||||
|
return TRUE;
|
||||||
|
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
GrdRdpLegacyBuffer *
|
||||||
|
grd_rdp_buffer_pool_acquire (GrdRdpBufferPool *buffer_pool)
|
||||||
|
{
|
||||||
|
g_autoptr (GMutexLocker) locker = NULL;
|
||||||
|
GHashTableIter iter;
|
||||||
|
GrdRdpLegacyBuffer *buffer;
|
||||||
|
BufferInfo *buffer_info;
|
||||||
|
gboolean buffer_found = FALSE;
|
||||||
|
|
||||||
|
g_assert (buffer_pool->buffer_height > 0);
|
||||||
|
g_assert (buffer_pool->buffer_stride > 0);
|
||||||
|
|
||||||
|
locker = g_mutex_locker_new (&buffer_pool->pool_mutex);
|
||||||
|
if (g_hash_table_size (buffer_pool->buffer_table) <= buffer_pool->buffers_taken &&
|
||||||
|
!add_buffer_to_pool (buffer_pool, FALSE))
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
g_hash_table_iter_init (&iter, buffer_pool->buffer_table);
|
||||||
|
while (g_hash_table_iter_next (&iter, (gpointer *) &buffer,
|
||||||
|
(gpointer *) &buffer_info))
|
||||||
|
{
|
||||||
|
if (!buffer_info->buffer_taken && !buffer_has_mapped_data (buffer))
|
||||||
|
{
|
||||||
|
buffer_found = TRUE;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!buffer_found)
|
||||||
|
{
|
||||||
|
g_hash_table_iter_init (&iter, buffer_pool->buffer_table);
|
||||||
|
while (g_hash_table_iter_next (&iter, (gpointer *) &buffer,
|
||||||
|
(gpointer *) &buffer_info))
|
||||||
|
{
|
||||||
|
if (!buffer_info->buffer_taken)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
g_assert (buffer);
|
||||||
|
|
||||||
|
buffer_info->buffer_taken = TRUE;
|
||||||
|
++buffer_pool->buffers_taken;
|
||||||
|
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
should_resize_buffer_pool (GrdRdpBufferPool *buffer_pool)
|
||||||
|
{
|
||||||
|
uint32_t buffers_taken = buffer_pool->buffers_taken;
|
||||||
|
uint32_t minimum_size = buffer_pool->minimum_pool_size;
|
||||||
|
uint32_t current_pool_size;
|
||||||
|
|
||||||
|
current_pool_size = g_hash_table_size (buffer_pool->buffer_table);
|
||||||
|
|
||||||
|
if (current_pool_size > minimum_size &&
|
||||||
|
current_pool_size > buffers_taken)
|
||||||
|
return TRUE;
|
||||||
|
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
grd_rdp_buffer_pool_release_buffer (GrdRdpBufferPool *buffer_pool,
|
||||||
|
GrdRdpLegacyBuffer *buffer)
|
||||||
|
{
|
||||||
|
BufferInfo *buffer_info;
|
||||||
|
gboolean queue_pool_resize;
|
||||||
|
gboolean queue_unmap;
|
||||||
|
|
||||||
|
g_mutex_lock (&buffer_pool->pool_mutex);
|
||||||
|
if (!g_hash_table_lookup_extended (buffer_pool->buffer_table, buffer,
|
||||||
|
NULL, (gpointer *) &buffer_info))
|
||||||
|
g_assert_not_reached ();
|
||||||
|
|
||||||
|
g_assert (buffer_info->buffer_taken);
|
||||||
|
|
||||||
|
buffer_info->buffer_taken = FALSE;
|
||||||
|
--buffer_pool->buffers_taken;
|
||||||
|
|
||||||
|
queue_pool_resize = should_resize_buffer_pool (buffer_pool);
|
||||||
|
queue_unmap = buffer_has_mapped_data (buffer);
|
||||||
|
g_mutex_unlock (&buffer_pool->pool_mutex);
|
||||||
|
|
||||||
|
if (queue_pool_resize)
|
||||||
|
g_source_set_ready_time (buffer_pool->resize_pool_source, 0);
|
||||||
|
if (queue_unmap)
|
||||||
|
g_source_set_ready_time (buffer_pool->unmap_source, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
resize_buffer_pool (gpointer user_data)
|
||||||
|
{
|
||||||
|
GrdRdpBufferPool *buffer_pool = user_data;
|
||||||
|
GHashTableIter iter;
|
||||||
|
BufferInfo *buffer_info;
|
||||||
|
|
||||||
|
g_mutex_lock (&buffer_pool->pool_mutex);
|
||||||
|
g_hash_table_iter_init (&iter, buffer_pool->buffer_table);
|
||||||
|
while (should_resize_buffer_pool (buffer_pool) &&
|
||||||
|
g_hash_table_iter_next (&iter, NULL, (gpointer *) &buffer_info))
|
||||||
|
{
|
||||||
|
if (!buffer_info->buffer_taken)
|
||||||
|
g_hash_table_iter_remove (&iter);
|
||||||
|
}
|
||||||
|
g_mutex_unlock (&buffer_pool->pool_mutex);
|
||||||
|
|
||||||
|
return G_SOURCE_CONTINUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
unmap_untaken_buffers (gpointer user_data)
|
||||||
|
{
|
||||||
|
GrdRdpBufferPool *buffer_pool = user_data;
|
||||||
|
GHashTableIter iter;
|
||||||
|
GrdRdpLegacyBuffer *buffer;
|
||||||
|
BufferInfo *buffer_info;
|
||||||
|
|
||||||
|
g_mutex_lock (&buffer_pool->pool_mutex);
|
||||||
|
g_hash_table_iter_init (&iter, buffer_pool->buffer_table);
|
||||||
|
while (g_hash_table_iter_next (&iter, (gpointer *) &buffer,
|
||||||
|
(gpointer *) &buffer_info))
|
||||||
|
{
|
||||||
|
if (!buffer_info->buffer_taken)
|
||||||
|
grd_rdp_legacy_buffer_queue_resource_unmap (buffer);
|
||||||
|
}
|
||||||
|
g_mutex_unlock (&buffer_pool->pool_mutex);
|
||||||
|
|
||||||
|
return G_SOURCE_CONTINUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
buffer_pool_source_dispatch (GSource *source,
|
||||||
|
GSourceFunc callback,
|
||||||
|
gpointer user_data)
|
||||||
|
{
|
||||||
|
g_source_set_ready_time (source, -1);
|
||||||
|
|
||||||
|
return callback (user_data);
|
||||||
|
}
|
||||||
|
|
||||||
|
static GSourceFuncs buffer_pool_source_funcs =
|
||||||
|
{
|
||||||
|
.dispatch = buffer_pool_source_dispatch,
|
||||||
|
};
|
||||||
|
|
||||||
|
GrdRdpBufferPool *
|
||||||
|
grd_rdp_buffer_pool_new (GrdRdpSurface *rdp_surface,
|
||||||
|
uint32_t minimum_size)
|
||||||
|
{
|
||||||
|
g_autoptr (GrdRdpBufferPool) buffer_pool = NULL;
|
||||||
|
|
||||||
|
buffer_pool = g_object_new (GRD_TYPE_RDP_BUFFER_POOL, NULL);
|
||||||
|
buffer_pool->rdp_surface = rdp_surface;
|
||||||
|
buffer_pool->minimum_pool_size = minimum_size;
|
||||||
|
|
||||||
|
buffer_pool->resize_pool_source = g_source_new (&buffer_pool_source_funcs,
|
||||||
|
sizeof (GSource));
|
||||||
|
g_source_set_callback (buffer_pool->resize_pool_source,
|
||||||
|
resize_buffer_pool, buffer_pool, NULL);
|
||||||
|
g_source_set_ready_time (buffer_pool->resize_pool_source, -1);
|
||||||
|
g_source_attach (buffer_pool->resize_pool_source, NULL);
|
||||||
|
|
||||||
|
buffer_pool->unmap_source = g_source_new (&buffer_pool_source_funcs,
|
||||||
|
sizeof (GSource));
|
||||||
|
g_source_set_callback (buffer_pool->unmap_source,
|
||||||
|
unmap_untaken_buffers, buffer_pool, NULL);
|
||||||
|
g_source_set_priority (buffer_pool->unmap_source, G_PRIORITY_LOW);
|
||||||
|
g_source_set_ready_time (buffer_pool->unmap_source, -1);
|
||||||
|
g_source_attach (buffer_pool->unmap_source, NULL);
|
||||||
|
|
||||||
|
return g_steal_pointer (&buffer_pool);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
on_sync_complete (gboolean success,
|
||||||
|
gpointer user_data)
|
||||||
|
{
|
||||||
|
GrdSyncPoint *sync_point = user_data;
|
||||||
|
|
||||||
|
grd_sync_point_complete (sync_point, success);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
sync_egl_thread (GrdRdpBufferPool *buffer_pool)
|
||||||
|
{
|
||||||
|
GrdRdpRenderer *renderer = buffer_pool->rdp_surface->renderer;
|
||||||
|
GrdSessionRdp *session_rdp = grd_rdp_renderer_get_session (renderer);
|
||||||
|
GrdRdpServer *rdp_server = grd_session_rdp_get_server (session_rdp);
|
||||||
|
GrdContext *context = grd_rdp_server_get_context (rdp_server);
|
||||||
|
GrdEglThread *egl_thread = grd_context_get_egl_thread (context);
|
||||||
|
GrdSyncPoint sync_point = {};
|
||||||
|
|
||||||
|
grd_sync_point_init (&sync_point);
|
||||||
|
grd_egl_thread_sync (egl_thread, on_sync_complete,
|
||||||
|
&sync_point, NULL);
|
||||||
|
|
||||||
|
grd_sync_point_wait_for_completion (&sync_point);
|
||||||
|
grd_sync_point_clear (&sync_point);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
grd_rdp_buffer_pool_dispose (GObject *object)
|
||||||
|
{
|
||||||
|
GrdRdpBufferPool *buffer_pool = GRD_RDP_BUFFER_POOL (object);
|
||||||
|
|
||||||
|
if (buffer_pool->unmap_source)
|
||||||
|
{
|
||||||
|
g_source_destroy (buffer_pool->unmap_source);
|
||||||
|
g_clear_pointer (&buffer_pool->unmap_source, g_source_unref);
|
||||||
|
}
|
||||||
|
if (buffer_pool->resize_pool_source)
|
||||||
|
{
|
||||||
|
g_source_destroy (buffer_pool->resize_pool_source);
|
||||||
|
g_clear_pointer (&buffer_pool->resize_pool_source, g_source_unref);
|
||||||
|
}
|
||||||
|
|
||||||
|
g_assert (buffer_pool->buffers_taken == 0);
|
||||||
|
|
||||||
|
G_OBJECT_CLASS (grd_rdp_buffer_pool_parent_class)->dispose (object);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
grd_rdp_buffer_pool_finalize (GObject *object)
|
||||||
|
{
|
||||||
|
GrdRdpBufferPool *buffer_pool = GRD_RDP_BUFFER_POOL (object);
|
||||||
|
GrdRdpRenderer *renderer = buffer_pool->rdp_surface->renderer;
|
||||||
|
GrdSessionRdp *session_rdp = grd_rdp_renderer_get_session (renderer);
|
||||||
|
GrdRdpServer *rdp_server = grd_session_rdp_get_server (session_rdp);
|
||||||
|
GrdContext *context = grd_rdp_server_get_context (rdp_server);
|
||||||
|
GrdEglThread *egl_thread = grd_context_get_egl_thread (context);
|
||||||
|
|
||||||
|
g_mutex_clear (&buffer_pool->pool_mutex);
|
||||||
|
|
||||||
|
g_clear_pointer (&buffer_pool->buffer_table, g_hash_table_unref);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* All buffers need to be destroyed, before the pool is freed to avoid use
|
||||||
|
* after free by the EGL thread, when the RDP server is shut down and with it
|
||||||
|
* the GrdHwAccelNvidia instance
|
||||||
|
*/
|
||||||
|
if (egl_thread)
|
||||||
|
sync_egl_thread (buffer_pool);
|
||||||
|
|
||||||
|
G_OBJECT_CLASS (grd_rdp_buffer_pool_parent_class)->finalize (object);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
grd_rdp_buffer_pool_init (GrdRdpBufferPool *buffer_pool)
|
||||||
|
{
|
||||||
|
buffer_pool->buffer_table =
|
||||||
|
g_hash_table_new_full (NULL, NULL,
|
||||||
|
(GDestroyNotify) grd_rdp_legacy_buffer_free,
|
||||||
|
g_free);
|
||||||
|
|
||||||
|
g_mutex_init (&buffer_pool->pool_mutex);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
grd_rdp_buffer_pool_class_init (GrdRdpBufferPoolClass *klass)
|
||||||
|
{
|
||||||
|
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
||||||
|
|
||||||
|
object_class->dispose = grd_rdp_buffer_pool_dispose;
|
||||||
|
object_class->finalize = grd_rdp_buffer_pool_finalize;
|
||||||
|
}
|
||||||
44
grd-rdp-buffer-pool.h
Normal file
44
grd-rdp-buffer-pool.h
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2022 Pascal Nowack
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License as
|
||||||
|
* published by the Free Software Foundation; either version 2 of the
|
||||||
|
* License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
|
||||||
|
* 02111-1307, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <ffnvcodec/dynlink_cuda.h>
|
||||||
|
#include <glib-object.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#include "grd-types.h"
|
||||||
|
|
||||||
|
#define GRD_TYPE_RDP_BUFFER_POOL (grd_rdp_buffer_pool_get_type ())
|
||||||
|
G_DECLARE_FINAL_TYPE (GrdRdpBufferPool, grd_rdp_buffer_pool,
|
||||||
|
GRD, RDP_BUFFER_POOL, GObject)
|
||||||
|
|
||||||
|
GrdRdpBufferPool *grd_rdp_buffer_pool_new (GrdRdpSurface *rdp_surface,
|
||||||
|
uint32_t minimum_size);
|
||||||
|
|
||||||
|
GrdRdpSurface *grd_rdp_buffer_pool_get_surface (GrdRdpBufferPool *buffer_pool);
|
||||||
|
|
||||||
|
gboolean grd_rdp_buffer_pool_resize_buffers (GrdRdpBufferPool *buffer_pool,
|
||||||
|
uint32_t buffer_height,
|
||||||
|
uint32_t buffer_stride);
|
||||||
|
|
||||||
|
GrdRdpLegacyBuffer *grd_rdp_buffer_pool_acquire (GrdRdpBufferPool *buffer_pool);
|
||||||
|
|
||||||
|
void grd_rdp_buffer_pool_release_buffer (GrdRdpBufferPool *buffer_pool,
|
||||||
|
GrdRdpLegacyBuffer *buffer);
|
||||||
172
grd-rdp-buffer.c
Normal file
172
grd-rdp-buffer.c
Normal file
@@ -0,0 +1,172 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2024 Pascal Nowack
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License as
|
||||||
|
* published by the Free Software Foundation; either version 2 of the
|
||||||
|
* License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
|
||||||
|
* 02111-1307, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
|
#include "grd-rdp-buffer.h"
|
||||||
|
|
||||||
|
#include <drm_fourcc.h>
|
||||||
|
|
||||||
|
#include "grd-rdp-buffer-info.h"
|
||||||
|
#include "grd-rdp-pw-buffer.h"
|
||||||
|
#include "grd-rdp-surface.h"
|
||||||
|
#include "grd-vk-utils.h"
|
||||||
|
|
||||||
|
struct _GrdRdpBuffer
|
||||||
|
{
|
||||||
|
GObject parent;
|
||||||
|
|
||||||
|
GrdRdpPwBuffer *rdp_pw_buffer;
|
||||||
|
GrdRdpBufferInfo *rdp_buffer_info;
|
||||||
|
|
||||||
|
GrdVkImage *dma_buf_image;
|
||||||
|
|
||||||
|
gboolean marked_for_removal;
|
||||||
|
};
|
||||||
|
|
||||||
|
G_DEFINE_TYPE (GrdRdpBuffer, grd_rdp_buffer, G_TYPE_OBJECT)
|
||||||
|
|
||||||
|
GrdRdpPwBuffer *
|
||||||
|
grd_rdp_buffer_get_rdp_pw_buffer (GrdRdpBuffer *rdp_buffer)
|
||||||
|
{
|
||||||
|
return rdp_buffer->rdp_pw_buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
const GrdRdpBufferInfo *
|
||||||
|
grd_rdp_buffer_get_rdp_buffer_info (GrdRdpBuffer *rdp_buffer)
|
||||||
|
{
|
||||||
|
return rdp_buffer->rdp_buffer_info;
|
||||||
|
}
|
||||||
|
|
||||||
|
GrdVkImage *
|
||||||
|
grd_rdp_buffer_get_dma_buf_image (GrdRdpBuffer *rdp_buffer)
|
||||||
|
{
|
||||||
|
return rdp_buffer->dma_buf_image;
|
||||||
|
}
|
||||||
|
|
||||||
|
gboolean
|
||||||
|
grd_rdp_buffer_is_marked_for_removal (GrdRdpBuffer *rdp_buffer)
|
||||||
|
{
|
||||||
|
return rdp_buffer->marked_for_removal;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
grd_rdp_buffer_mark_for_removal (GrdRdpBuffer *rdp_buffer)
|
||||||
|
{
|
||||||
|
rdp_buffer->marked_for_removal = TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
import_dma_buf_image (GrdRdpBuffer *rdp_buffer,
|
||||||
|
GrdRdpPwBuffer *rdp_pw_buffer,
|
||||||
|
GrdRdpBufferInfo *rdp_buffer_info,
|
||||||
|
GrdRdpSurface *rdp_surface,
|
||||||
|
GrdVkDevice *vk_device,
|
||||||
|
GError **error)
|
||||||
|
{
|
||||||
|
uint32_t surface_width = grd_rdp_surface_get_width (rdp_surface);
|
||||||
|
uint32_t surface_height = grd_rdp_surface_get_height (rdp_surface);
|
||||||
|
const GrdRdpPwBufferDmaBufInfo *dma_buf_info =
|
||||||
|
grd_rdp_pw_buffer_get_dma_buf_info (rdp_pw_buffer);
|
||||||
|
VkFormat vk_format = VK_FORMAT_UNDEFINED;
|
||||||
|
|
||||||
|
if (!grd_vk_get_vk_format_from_drm_format (rdp_buffer_info->drm_format,
|
||||||
|
&vk_format, error))
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
rdp_buffer->dma_buf_image =
|
||||||
|
grd_vk_dma_buf_image_new (vk_device, vk_format,
|
||||||
|
surface_width, surface_height,
|
||||||
|
VK_IMAGE_USAGE_SAMPLED_BIT,
|
||||||
|
dma_buf_info->fd,
|
||||||
|
dma_buf_info->offset,
|
||||||
|
dma_buf_info->stride,
|
||||||
|
rdp_buffer_info->drm_format_modifier,
|
||||||
|
error);
|
||||||
|
if (!rdp_buffer->dma_buf_image)
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
rdp_buffer_info->has_vk_image = TRUE;
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
GrdRdpBuffer *
|
||||||
|
grd_rdp_buffer_new (GrdRdpPwBuffer *rdp_pw_buffer,
|
||||||
|
GrdRdpBufferInfo *rdp_buffer_info,
|
||||||
|
GrdRdpSurface *rdp_surface,
|
||||||
|
GrdVkDevice *vk_device,
|
||||||
|
GError **error)
|
||||||
|
{
|
||||||
|
g_autoptr (GrdRdpBuffer) rdp_buffer = NULL;
|
||||||
|
GrdRdpBufferType buffer_type;
|
||||||
|
|
||||||
|
rdp_buffer = g_object_new (GRD_TYPE_RDP_BUFFER, NULL);
|
||||||
|
rdp_buffer->rdp_pw_buffer = rdp_pw_buffer;
|
||||||
|
|
||||||
|
buffer_type = grd_rdp_pw_buffer_get_buffer_type (rdp_pw_buffer);
|
||||||
|
if (buffer_type == GRD_RDP_BUFFER_TYPE_DMA_BUF &&
|
||||||
|
vk_device &&
|
||||||
|
rdp_buffer_info->drm_format_modifier != DRM_FORMAT_MOD_INVALID)
|
||||||
|
{
|
||||||
|
g_autoptr (GError) local_error = NULL;
|
||||||
|
|
||||||
|
if (!import_dma_buf_image (rdp_buffer, rdp_pw_buffer, rdp_buffer_info,
|
||||||
|
rdp_surface, vk_device, &local_error))
|
||||||
|
{
|
||||||
|
if (rdp_buffer_info->has_vk_image)
|
||||||
|
{
|
||||||
|
g_propagate_error (error, g_steal_pointer (&local_error));
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
g_debug ("[RDP] Could not import dma-buf image: %s",
|
||||||
|
local_error->message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
rdp_buffer->rdp_buffer_info =
|
||||||
|
g_memdup2 (rdp_buffer_info, sizeof (GrdRdpBufferInfo));
|
||||||
|
|
||||||
|
return g_steal_pointer (&rdp_buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
grd_rdp_buffer_dispose (GObject *object)
|
||||||
|
{
|
||||||
|
GrdRdpBuffer *rdp_buffer = GRD_RDP_BUFFER (object);
|
||||||
|
|
||||||
|
g_clear_object (&rdp_buffer->dma_buf_image);
|
||||||
|
g_clear_pointer (&rdp_buffer->rdp_buffer_info, g_free);
|
||||||
|
|
||||||
|
G_OBJECT_CLASS (grd_rdp_buffer_parent_class)->dispose (object);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
grd_rdp_buffer_init (GrdRdpBuffer *rdp_buffer)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
grd_rdp_buffer_class_init (GrdRdpBufferClass *klass)
|
||||||
|
{
|
||||||
|
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
||||||
|
|
||||||
|
object_class->dispose = grd_rdp_buffer_dispose;
|
||||||
|
}
|
||||||
44
grd-rdp-buffer.h
Normal file
44
grd-rdp-buffer.h
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2024 Pascal Nowack
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License as
|
||||||
|
* published by the Free Software Foundation; either version 2 of the
|
||||||
|
* License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
|
||||||
|
* 02111-1307, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <glib-object.h>
|
||||||
|
|
||||||
|
#include "grd-types.h"
|
||||||
|
|
||||||
|
#define GRD_TYPE_RDP_BUFFER (grd_rdp_buffer_get_type ())
|
||||||
|
G_DECLARE_FINAL_TYPE (GrdRdpBuffer, grd_rdp_buffer,
|
||||||
|
GRD, RDP_BUFFER, GObject)
|
||||||
|
|
||||||
|
GrdRdpBuffer *grd_rdp_buffer_new (GrdRdpPwBuffer *rdp_pw_buffer,
|
||||||
|
GrdRdpBufferInfo *rdp_buffer_info,
|
||||||
|
GrdRdpSurface *rdp_surface,
|
||||||
|
GrdVkDevice *vk_device,
|
||||||
|
GError **error);
|
||||||
|
|
||||||
|
GrdRdpPwBuffer *grd_rdp_buffer_get_rdp_pw_buffer (GrdRdpBuffer *rdp_buffer);
|
||||||
|
|
||||||
|
const GrdRdpBufferInfo *grd_rdp_buffer_get_rdp_buffer_info (GrdRdpBuffer *rdp_buffer);
|
||||||
|
|
||||||
|
GrdVkImage *grd_rdp_buffer_get_dma_buf_image (GrdRdpBuffer *rdp_buffer);
|
||||||
|
|
||||||
|
gboolean grd_rdp_buffer_is_marked_for_removal (GrdRdpBuffer *rdp_buffer);
|
||||||
|
|
||||||
|
void grd_rdp_buffer_mark_for_removal (GrdRdpBuffer *rdp_buffer);
|
||||||
1079
grd-rdp-camera-stream.c
Normal file
1079
grd-rdp-camera-stream.c
Normal file
File diff suppressed because it is too large
Load Diff
53
grd-rdp-camera-stream.h
Normal file
53
grd-rdp-camera-stream.h
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2025 Pascal Nowack
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License as
|
||||||
|
* published by the Free Software Foundation; either version 2 of the
|
||||||
|
* License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
|
||||||
|
* 02111-1307, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <glib-object.h>
|
||||||
|
#include <pipewire/pipewire.h>
|
||||||
|
|
||||||
|
#include "grd-types.h"
|
||||||
|
|
||||||
|
#define GRD_TYPE_RDP_CAMERA_STREAM (grd_rdp_camera_stream_get_type ())
|
||||||
|
G_DECLARE_FINAL_TYPE (GrdRdpCameraStream, grd_rdp_camera_stream,
|
||||||
|
GRD, RDP_CAMERA_STREAM, GObject)
|
||||||
|
|
||||||
|
GrdRdpCameraStream *grd_rdp_camera_stream_new (GrdRdpDvcCameraDevice *device,
|
||||||
|
GMainContext *camera_context,
|
||||||
|
struct pw_core *pipewire_core,
|
||||||
|
const char *device_name,
|
||||||
|
uint8_t stream_index,
|
||||||
|
GList *media_type_descriptions,
|
||||||
|
GError **error);
|
||||||
|
|
||||||
|
uint8_t grd_rdp_camera_stream_get_stream_index (GrdRdpCameraStream *camera_stream);
|
||||||
|
|
||||||
|
gboolean grd_rdp_camera_stream_notify_stream_started (GrdRdpCameraStream *camera_stream,
|
||||||
|
uint32_t run_sequence);
|
||||||
|
|
||||||
|
void grd_rdp_camera_stream_inhibit_camera_loop (GrdRdpCameraStream *camera_stream);
|
||||||
|
|
||||||
|
void grd_rdp_camera_stream_uninhibit_camera_loop (GrdRdpCameraStream *camera_stream);
|
||||||
|
|
||||||
|
gboolean grd_rdp_camera_stream_announce_new_sample (GrdRdpCameraStream *camera_stream,
|
||||||
|
GrdSampleBuffer *sample_buffer);
|
||||||
|
|
||||||
|
void grd_rdp_camera_stream_submit_sample (GrdRdpCameraStream *camera_stream,
|
||||||
|
GrdSampleBuffer *sample_buffer,
|
||||||
|
gboolean success);
|
||||||
643
grd-rdp-connect-time-autodetection.c
Normal file
643
grd-rdp-connect-time-autodetection.c
Normal file
@@ -0,0 +1,643 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2023 Pascal Nowack
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License as
|
||||||
|
* published by the Free Software Foundation; either version 2 of the
|
||||||
|
* License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
|
||||||
|
* 02111-1307, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
|
#include "grd-rdp-connect-time-autodetection.h"
|
||||||
|
|
||||||
|
#define CT_BW_MAX_TOLERABLE_RESPONSE_LATENCY_US (400 * 1000)
|
||||||
|
#define CT_BW_MAX_TOLERABLE_TIME_DELTA_MS 100
|
||||||
|
|
||||||
|
#define CT_N_PINGS 10
|
||||||
|
#define CT_PING_INTERVAL_MS 10
|
||||||
|
|
||||||
|
#define BW_MEASURE_SEQUENCE_NUMBER 0
|
||||||
|
#define CT_NON_RTT_SEQUENCE_NUMBER 0
|
||||||
|
|
||||||
|
typedef enum
|
||||||
|
{
|
||||||
|
CT_AUTODETECT_STATE_NONE,
|
||||||
|
CT_AUTODETECT_STATE_MEASURE_BW_1,
|
||||||
|
CT_AUTODETECT_STATE_MEASURE_BW_2,
|
||||||
|
CT_AUTODETECT_STATE_MEASURE_BW_3,
|
||||||
|
CT_AUTODETECT_STATE_AWAIT_BW_RESULT_1,
|
||||||
|
CT_AUTODETECT_STATE_AWAIT_BW_RESULT_2,
|
||||||
|
CT_AUTODETECT_STATE_AWAIT_BW_RESULT_3,
|
||||||
|
CT_AUTODETECT_STATE_START_RTT_DETECTION,
|
||||||
|
CT_AUTODETECT_STATE_AWAIT_LAST_RTT_RESPONSE,
|
||||||
|
CT_AUTODETECT_STATE_SEND_NET_CHAR_RESULT,
|
||||||
|
CT_AUTODETECT_STATE_COMPLETE,
|
||||||
|
} CtAutodetectState;
|
||||||
|
|
||||||
|
struct _GrdRdpConnectTimeAutodetection
|
||||||
|
{
|
||||||
|
GObject parent;
|
||||||
|
|
||||||
|
GrdRdpNetworkAutodetection *network_autodetection;
|
||||||
|
rdpAutoDetect *rdp_autodetect;
|
||||||
|
gboolean protocol_stopped;
|
||||||
|
|
||||||
|
GMutex ct_autodetection_mutex;
|
||||||
|
CtAutodetectState state;
|
||||||
|
|
||||||
|
GSource *sync_source;
|
||||||
|
GCond net_char_sync_cond;
|
||||||
|
GMutex net_char_sync_mutex;
|
||||||
|
gboolean pending_net_char_sync;
|
||||||
|
|
||||||
|
GSource *ping_source;
|
||||||
|
uint32_t pending_pings;
|
||||||
|
gboolean pending_last_sequence_number;
|
||||||
|
uint16_t last_sequence_number;
|
||||||
|
|
||||||
|
int64_t base_round_trip_time_us;
|
||||||
|
int64_t avg_round_trip_time_us;
|
||||||
|
|
||||||
|
int64_t bw_measure_time_us;
|
||||||
|
|
||||||
|
uint32_t last_time_delta_ms;
|
||||||
|
uint32_t last_byte_count;
|
||||||
|
};
|
||||||
|
|
||||||
|
G_DEFINE_TYPE (GrdRdpConnectTimeAutodetection, grd_rdp_connect_time_autodetection,
|
||||||
|
G_TYPE_OBJECT)
|
||||||
|
|
||||||
|
gboolean
|
||||||
|
grd_rdp_connect_time_autodetection_is_complete (GrdRdpConnectTimeAutodetection *ct_autodetection)
|
||||||
|
{
|
||||||
|
g_autoptr (GMutexLocker) locker = NULL;
|
||||||
|
|
||||||
|
locker = g_mutex_locker_new (&ct_autodetection->ct_autodetection_mutex);
|
||||||
|
return ct_autodetection->state == CT_AUTODETECT_STATE_COMPLETE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
determine_bw_measure_payloads (GrdRdpConnectTimeAutodetection *ct_autodetection,
|
||||||
|
uint16_t *payload_lengths,
|
||||||
|
uint32_t *n_payloads)
|
||||||
|
{
|
||||||
|
switch (ct_autodetection->state)
|
||||||
|
{
|
||||||
|
case CT_AUTODETECT_STATE_MEASURE_BW_1:
|
||||||
|
*payload_lengths = 15 * 1024 + 512 + 256 + 128 + 64;
|
||||||
|
*n_payloads = 1;
|
||||||
|
break;
|
||||||
|
case CT_AUTODETECT_STATE_MEASURE_BW_2:
|
||||||
|
*payload_lengths = 15 * 1024 + 512 + 256 + 128 + 64;
|
||||||
|
*n_payloads = 4;
|
||||||
|
break;
|
||||||
|
case CT_AUTODETECT_STATE_MEASURE_BW_3:
|
||||||
|
*payload_lengths = 15 * 1024 + 512 + 256 + 128 + 64;
|
||||||
|
*n_payloads = 16;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
g_assert_not_reached ();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static CtAutodetectState
|
||||||
|
get_next_bw_measure_state (GrdRdpConnectTimeAutodetection *ct_autodetection)
|
||||||
|
{
|
||||||
|
switch (ct_autodetection->state)
|
||||||
|
{
|
||||||
|
case CT_AUTODETECT_STATE_MEASURE_BW_1:
|
||||||
|
return CT_AUTODETECT_STATE_AWAIT_BW_RESULT_1;
|
||||||
|
case CT_AUTODETECT_STATE_MEASURE_BW_2:
|
||||||
|
return CT_AUTODETECT_STATE_AWAIT_BW_RESULT_2;
|
||||||
|
case CT_AUTODETECT_STATE_MEASURE_BW_3:
|
||||||
|
return CT_AUTODETECT_STATE_AWAIT_BW_RESULT_3;
|
||||||
|
case CT_AUTODETECT_STATE_AWAIT_BW_RESULT_1:
|
||||||
|
return CT_AUTODETECT_STATE_MEASURE_BW_2;
|
||||||
|
case CT_AUTODETECT_STATE_AWAIT_BW_RESULT_2:
|
||||||
|
return CT_AUTODETECT_STATE_MEASURE_BW_3;
|
||||||
|
default:
|
||||||
|
g_assert_not_reached ();
|
||||||
|
}
|
||||||
|
|
||||||
|
return CT_AUTODETECT_STATE_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
measure_connect_time_bandwidth (GrdRdpConnectTimeAutodetection *ct_autodetection,
|
||||||
|
uint16_t payload_lengths,
|
||||||
|
uint32_t n_payloads)
|
||||||
|
{
|
||||||
|
rdpAutoDetect *rdp_autodetect = ct_autodetection->rdp_autodetect;
|
||||||
|
uint32_t i;
|
||||||
|
|
||||||
|
g_assert (payload_lengths > 0);
|
||||||
|
g_assert (n_payloads >= 1);
|
||||||
|
|
||||||
|
rdp_autodetect->BandwidthMeasureStart (rdp_autodetect, RDP_TRANSPORT_TCP,
|
||||||
|
BW_MEASURE_SEQUENCE_NUMBER);
|
||||||
|
for (i = 0; i < n_payloads - 1; ++i)
|
||||||
|
{
|
||||||
|
rdp_autodetect->BandwidthMeasurePayload (rdp_autodetect,
|
||||||
|
RDP_TRANSPORT_TCP,
|
||||||
|
BW_MEASURE_SEQUENCE_NUMBER,
|
||||||
|
payload_lengths);
|
||||||
|
}
|
||||||
|
rdp_autodetect->BandwidthMeasureStop (rdp_autodetect, RDP_TRANSPORT_TCP,
|
||||||
|
BW_MEASURE_SEQUENCE_NUMBER,
|
||||||
|
payload_lengths);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const char *
|
||||||
|
ct_autodetect_state_to_string (CtAutodetectState state)
|
||||||
|
{
|
||||||
|
switch (state)
|
||||||
|
{
|
||||||
|
case CT_AUTODETECT_STATE_NONE:
|
||||||
|
return "NONE";
|
||||||
|
case CT_AUTODETECT_STATE_MEASURE_BW_1:
|
||||||
|
return "MEASURE_BW_1";
|
||||||
|
case CT_AUTODETECT_STATE_MEASURE_BW_2:
|
||||||
|
return "MEASURE_BW_2";
|
||||||
|
case CT_AUTODETECT_STATE_MEASURE_BW_3:
|
||||||
|
return "MEASURE_BW_3";
|
||||||
|
case CT_AUTODETECT_STATE_AWAIT_BW_RESULT_1:
|
||||||
|
return "AWAIT_BW_RESULT_1";
|
||||||
|
case CT_AUTODETECT_STATE_AWAIT_BW_RESULT_2:
|
||||||
|
return "AWAIT_BW_RESULT_2";
|
||||||
|
case CT_AUTODETECT_STATE_AWAIT_BW_RESULT_3:
|
||||||
|
return "AWAIT_BW_RESULT_3";
|
||||||
|
case CT_AUTODETECT_STATE_START_RTT_DETECTION:
|
||||||
|
return "START_RTT_DETECTION";
|
||||||
|
case CT_AUTODETECT_STATE_AWAIT_LAST_RTT_RESPONSE:
|
||||||
|
return "AWAIT_LAST_RTT_RESPONSE";
|
||||||
|
case CT_AUTODETECT_STATE_SEND_NET_CHAR_RESULT:
|
||||||
|
return "SEND_NET_CHAR_RESULT";
|
||||||
|
case CT_AUTODETECT_STATE_COMPLETE:
|
||||||
|
return "COMPLETE";
|
||||||
|
}
|
||||||
|
|
||||||
|
g_assert_not_reached ();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
transition_to_state (GrdRdpConnectTimeAutodetection *ct_autodetection,
|
||||||
|
CtAutodetectState state)
|
||||||
|
{
|
||||||
|
g_assert (ct_autodetection->state != state);
|
||||||
|
|
||||||
|
g_debug ("[RDP] Connect-Time Autodetect: Updating state from %s to %s",
|
||||||
|
ct_autodetect_state_to_string (ct_autodetection->state),
|
||||||
|
ct_autodetect_state_to_string (state));
|
||||||
|
|
||||||
|
ct_autodetection->state = state;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
last_sequence_number_ready (gpointer user_data,
|
||||||
|
uint16_t sequence_number)
|
||||||
|
{
|
||||||
|
GrdRdpConnectTimeAutodetection *ct_autodetection = user_data;
|
||||||
|
|
||||||
|
g_mutex_lock (&ct_autodetection->ct_autodetection_mutex);
|
||||||
|
ct_autodetection->last_sequence_number = sequence_number;
|
||||||
|
ct_autodetection->pending_last_sequence_number = FALSE;
|
||||||
|
g_mutex_unlock (&ct_autodetection->ct_autodetection_mutex);
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
emit_ping (gpointer user_data)
|
||||||
|
{
|
||||||
|
GrdRdpConnectTimeAutodetection *ct_autodetection = user_data;
|
||||||
|
GrdRdpNetworkAutodetection *network_autodetection =
|
||||||
|
ct_autodetection->network_autodetection;
|
||||||
|
GrdRdpNwAutodetectSequenceNumberReadyCallback sequence_number_ready = NULL;
|
||||||
|
|
||||||
|
g_assert (!ct_autodetection->protocol_stopped);
|
||||||
|
|
||||||
|
g_assert (ct_autodetection->pending_pings > 0);
|
||||||
|
--ct_autodetection->pending_pings;
|
||||||
|
|
||||||
|
if (ct_autodetection->pending_pings == 0)
|
||||||
|
{
|
||||||
|
g_mutex_lock (&ct_autodetection->ct_autodetection_mutex);
|
||||||
|
g_clear_pointer (&ct_autodetection->ping_source, g_source_unref);
|
||||||
|
g_mutex_unlock (&ct_autodetection->ct_autodetection_mutex);
|
||||||
|
|
||||||
|
sequence_number_ready = last_sequence_number_ready;
|
||||||
|
}
|
||||||
|
|
||||||
|
grd_rdp_network_autodetection_emit_ping (network_autodetection,
|
||||||
|
sequence_number_ready,
|
||||||
|
ct_autodetection);
|
||||||
|
|
||||||
|
if (ct_autodetection->pending_pings == 0)
|
||||||
|
return G_SOURCE_REMOVE;
|
||||||
|
|
||||||
|
return G_SOURCE_CONTINUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
start_ping_emission (GrdRdpConnectTimeAutodetection *ct_autodetection)
|
||||||
|
{
|
||||||
|
emit_ping (ct_autodetection);
|
||||||
|
|
||||||
|
g_mutex_lock (&ct_autodetection->ct_autodetection_mutex);
|
||||||
|
ct_autodetection->ping_source = g_timeout_source_new (CT_PING_INTERVAL_MS);
|
||||||
|
|
||||||
|
g_source_set_callback (ct_autodetection->ping_source, emit_ping,
|
||||||
|
ct_autodetection, NULL);
|
||||||
|
g_source_attach (ct_autodetection->ping_source, NULL);
|
||||||
|
g_mutex_unlock (&ct_autodetection->ct_autodetection_mutex);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
notify_network_characteristics_result (GrdRdpConnectTimeAutodetection *ct_autodetection)
|
||||||
|
{
|
||||||
|
rdpAutoDetect *rdp_autodetect = ct_autodetection->rdp_autodetect;
|
||||||
|
rdpNetworkCharacteristicsResult result = {};
|
||||||
|
uint64_t bit_count;
|
||||||
|
uint32_t bandwidth_kbits;
|
||||||
|
|
||||||
|
bit_count = ((uint64_t) ct_autodetection->last_byte_count) * UINT64_C (8);
|
||||||
|
bandwidth_kbits = bit_count / MAX (ct_autodetection->last_time_delta_ms, 1);
|
||||||
|
|
||||||
|
result.type = RDP_NETCHAR_RESULT_TYPE_BASE_RTT_BW_AVG_RTT;
|
||||||
|
result.baseRTT = ct_autodetection->base_round_trip_time_us / 1000;
|
||||||
|
result.averageRTT = ct_autodetection->avg_round_trip_time_us / 1000;
|
||||||
|
result.bandwidth = bandwidth_kbits;
|
||||||
|
|
||||||
|
rdp_autodetect->NetworkCharacteristicsResult (rdp_autodetect,
|
||||||
|
RDP_TRANSPORT_TCP,
|
||||||
|
CT_NON_RTT_SEQUENCE_NUMBER,
|
||||||
|
&result);
|
||||||
|
}
|
||||||
|
|
||||||
|
static FREERDP_AUTODETECT_STATE
|
||||||
|
detect_network_characteristics (GrdRdpConnectTimeAutodetection *ct_autodetection)
|
||||||
|
{
|
||||||
|
CtAutodetectState new_state = CT_AUTODETECT_STATE_NONE;
|
||||||
|
g_autoptr (GMutexLocker) locker = NULL;
|
||||||
|
gboolean pending_bw_request = FALSE;
|
||||||
|
gboolean pending_ping_emission = FALSE;
|
||||||
|
gboolean pending_net_char_result = FALSE;
|
||||||
|
uint16_t payload_lengths = 0;
|
||||||
|
uint32_t n_payloads = 1;
|
||||||
|
|
||||||
|
locker = g_mutex_locker_new (&ct_autodetection->ct_autodetection_mutex);
|
||||||
|
switch (ct_autodetection->state)
|
||||||
|
{
|
||||||
|
case CT_AUTODETECT_STATE_NONE:
|
||||||
|
g_assert_not_reached ();
|
||||||
|
case CT_AUTODETECT_STATE_MEASURE_BW_1:
|
||||||
|
case CT_AUTODETECT_STATE_MEASURE_BW_2:
|
||||||
|
case CT_AUTODETECT_STATE_MEASURE_BW_3:
|
||||||
|
determine_bw_measure_payloads (ct_autodetection,
|
||||||
|
&payload_lengths, &n_payloads);
|
||||||
|
ct_autodetection->bw_measure_time_us = g_get_monotonic_time ();
|
||||||
|
new_state = get_next_bw_measure_state (ct_autodetection);
|
||||||
|
pending_bw_request = TRUE;
|
||||||
|
break;
|
||||||
|
case CT_AUTODETECT_STATE_AWAIT_BW_RESULT_1:
|
||||||
|
case CT_AUTODETECT_STATE_AWAIT_BW_RESULT_2:
|
||||||
|
case CT_AUTODETECT_STATE_AWAIT_BW_RESULT_3:
|
||||||
|
return FREERDP_AUTODETECT_STATE_REQUEST;
|
||||||
|
case CT_AUTODETECT_STATE_START_RTT_DETECTION:
|
||||||
|
g_assert (!ct_autodetection->ping_source);
|
||||||
|
g_assert (ct_autodetection->pending_pings >= 2);
|
||||||
|
|
||||||
|
new_state = CT_AUTODETECT_STATE_AWAIT_LAST_RTT_RESPONSE;
|
||||||
|
pending_ping_emission = TRUE;
|
||||||
|
break;
|
||||||
|
case CT_AUTODETECT_STATE_AWAIT_LAST_RTT_RESPONSE:
|
||||||
|
return FREERDP_AUTODETECT_STATE_REQUEST;
|
||||||
|
case CT_AUTODETECT_STATE_SEND_NET_CHAR_RESULT:
|
||||||
|
new_state = CT_AUTODETECT_STATE_COMPLETE;
|
||||||
|
pending_net_char_result = TRUE;
|
||||||
|
break;
|
||||||
|
case CT_AUTODETECT_STATE_COMPLETE:
|
||||||
|
return FREERDP_AUTODETECT_STATE_COMPLETE;
|
||||||
|
}
|
||||||
|
|
||||||
|
g_assert (new_state != CT_AUTODETECT_STATE_NONE);
|
||||||
|
transition_to_state (ct_autodetection, new_state);
|
||||||
|
g_clear_pointer (&locker, g_mutex_locker_free);
|
||||||
|
|
||||||
|
if (pending_bw_request)
|
||||||
|
{
|
||||||
|
measure_connect_time_bandwidth (ct_autodetection, payload_lengths,
|
||||||
|
n_payloads);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pending_ping_emission)
|
||||||
|
start_ping_emission (ct_autodetection);
|
||||||
|
|
||||||
|
if (pending_net_char_result)
|
||||||
|
{
|
||||||
|
notify_network_characteristics_result (ct_autodetection);
|
||||||
|
return FREERDP_AUTODETECT_STATE_COMPLETE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return FREERDP_AUTODETECT_STATE_REQUEST;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
grd_rdp_connect_time_autodetection_start_detection (GrdRdpConnectTimeAutodetection *ct_autodetection)
|
||||||
|
{
|
||||||
|
g_mutex_lock (&ct_autodetection->ct_autodetection_mutex);
|
||||||
|
g_assert (ct_autodetection->state == CT_AUTODETECT_STATE_NONE);
|
||||||
|
|
||||||
|
transition_to_state (ct_autodetection, CT_AUTODETECT_STATE_MEASURE_BW_1);
|
||||||
|
g_mutex_unlock (&ct_autodetection->ct_autodetection_mutex);
|
||||||
|
|
||||||
|
detect_network_characteristics (ct_autodetection);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
grd_rdp_connect_time_autodetection_invoke_shutdown (GrdRdpConnectTimeAutodetection *ct_autodetection)
|
||||||
|
{
|
||||||
|
g_mutex_lock (&ct_autodetection->net_char_sync_mutex);
|
||||||
|
ct_autodetection->protocol_stopped = TRUE;
|
||||||
|
g_cond_signal (&ct_autodetection->net_char_sync_cond);
|
||||||
|
g_mutex_unlock (&ct_autodetection->net_char_sync_mutex);
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint32_t
|
||||||
|
get_bw_measure_phase (GrdRdpConnectTimeAutodetection *ct_autodetection)
|
||||||
|
{
|
||||||
|
switch (ct_autodetection->state)
|
||||||
|
{
|
||||||
|
case CT_AUTODETECT_STATE_AWAIT_BW_RESULT_1:
|
||||||
|
return 1;
|
||||||
|
case CT_AUTODETECT_STATE_AWAIT_BW_RESULT_2:
|
||||||
|
return 2;
|
||||||
|
case CT_AUTODETECT_STATE_AWAIT_BW_RESULT_3:
|
||||||
|
return 3;
|
||||||
|
default:
|
||||||
|
g_assert_not_reached ();
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOL
|
||||||
|
grd_rdp_connect_time_autodetection_notify_bw_measure_results (GrdRdpConnectTimeAutodetection *ct_autodetection,
|
||||||
|
uint32_t time_delta_ms,
|
||||||
|
uint32_t byte_count)
|
||||||
|
{
|
||||||
|
CtAutodetectState new_state = CT_AUTODETECT_STATE_NONE;
|
||||||
|
g_autoptr (GMutexLocker) locker = NULL;
|
||||||
|
int64_t current_time_us;
|
||||||
|
int64_t response_latency_us;
|
||||||
|
|
||||||
|
locker = g_mutex_locker_new (&ct_autodetection->ct_autodetection_mutex);
|
||||||
|
switch (ct_autodetection->state)
|
||||||
|
{
|
||||||
|
case CT_AUTODETECT_STATE_AWAIT_BW_RESULT_1:
|
||||||
|
case CT_AUTODETECT_STATE_AWAIT_BW_RESULT_2:
|
||||||
|
current_time_us = g_get_monotonic_time ();
|
||||||
|
response_latency_us = current_time_us - ct_autodetection->bw_measure_time_us;
|
||||||
|
|
||||||
|
if (response_latency_us < CT_BW_MAX_TOLERABLE_RESPONSE_LATENCY_US &&
|
||||||
|
time_delta_ms < CT_BW_MAX_TOLERABLE_TIME_DELTA_MS)
|
||||||
|
new_state = get_next_bw_measure_state (ct_autodetection);
|
||||||
|
else
|
||||||
|
new_state = CT_AUTODETECT_STATE_START_RTT_DETECTION;
|
||||||
|
break;
|
||||||
|
case CT_AUTODETECT_STATE_AWAIT_BW_RESULT_3:
|
||||||
|
new_state = CT_AUTODETECT_STATE_START_RTT_DETECTION;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
g_warning ("[RDP] Connect-Time Autodetect: Received stray Bandwidth "
|
||||||
|
"Measure Results PDU (current state: %s)",
|
||||||
|
ct_autodetect_state_to_string (ct_autodetection->state));
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
g_debug ("[RDP] Connect-Time Autodetect: BW Measure Phase %u: "
|
||||||
|
"time delta: %ums, byte count: %u => %uKB/s",
|
||||||
|
get_bw_measure_phase (ct_autodetection), time_delta_ms, byte_count,
|
||||||
|
byte_count / MAX (time_delta_ms, 1));
|
||||||
|
|
||||||
|
ct_autodetection->last_time_delta_ms = time_delta_ms;
|
||||||
|
ct_autodetection->last_byte_count = byte_count;
|
||||||
|
|
||||||
|
g_assert (new_state != CT_AUTODETECT_STATE_NONE);
|
||||||
|
transition_to_state (ct_autodetection, new_state);
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
grd_rdp_connect_time_autodetection_notify_rtt_measure_response (GrdRdpConnectTimeAutodetection *ct_autodetection,
|
||||||
|
uint16_t sequence_number,
|
||||||
|
int64_t base_round_trip_time_us,
|
||||||
|
int64_t avg_round_trip_time_us)
|
||||||
|
{
|
||||||
|
g_autoptr (GMutexLocker) locker = NULL;
|
||||||
|
|
||||||
|
locker = g_mutex_locker_new (&ct_autodetection->ct_autodetection_mutex);
|
||||||
|
if (ct_autodetection->pending_last_sequence_number ||
|
||||||
|
ct_autodetection->last_sequence_number != sequence_number)
|
||||||
|
return;
|
||||||
|
|
||||||
|
g_assert (ct_autodetection->state == CT_AUTODETECT_STATE_AWAIT_LAST_RTT_RESPONSE);
|
||||||
|
|
||||||
|
g_debug ("[RDP] Connect-Time Autodetect: base RTT: %lims, average RTT: %lims",
|
||||||
|
base_round_trip_time_us / 1000, avg_round_trip_time_us / 1000);
|
||||||
|
|
||||||
|
ct_autodetection->base_round_trip_time_us = base_round_trip_time_us;
|
||||||
|
ct_autodetection->avg_round_trip_time_us = avg_round_trip_time_us;
|
||||||
|
|
||||||
|
transition_to_state (ct_autodetection, CT_AUTODETECT_STATE_SEND_NET_CHAR_RESULT);
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
sync_with_main_context (gpointer user_data)
|
||||||
|
{
|
||||||
|
GrdRdpConnectTimeAutodetection *ct_autodetection = user_data;
|
||||||
|
|
||||||
|
g_mutex_lock (&ct_autodetection->ct_autodetection_mutex);
|
||||||
|
if (ct_autodetection->ping_source)
|
||||||
|
{
|
||||||
|
g_source_destroy (ct_autodetection->ping_source);
|
||||||
|
g_clear_pointer (&ct_autodetection->ping_source, g_source_unref);
|
||||||
|
}
|
||||||
|
g_mutex_unlock (&ct_autodetection->ct_autodetection_mutex);
|
||||||
|
|
||||||
|
g_mutex_lock (&ct_autodetection->net_char_sync_mutex);
|
||||||
|
g_clear_pointer (&ct_autodetection->sync_source, g_source_unref);
|
||||||
|
|
||||||
|
ct_autodetection->pending_net_char_sync = FALSE;
|
||||||
|
g_cond_signal (&ct_autodetection->net_char_sync_cond);
|
||||||
|
g_mutex_unlock (&ct_autodetection->net_char_sync_mutex);
|
||||||
|
|
||||||
|
return G_SOURCE_REMOVE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
sync_ct_autodetect_handling (GrdRdpConnectTimeAutodetection *ct_autodetection)
|
||||||
|
{
|
||||||
|
g_mutex_lock (&ct_autodetection->net_char_sync_mutex);
|
||||||
|
ct_autodetection->pending_net_char_sync = TRUE;
|
||||||
|
|
||||||
|
ct_autodetection->sync_source = g_idle_source_new ();
|
||||||
|
g_source_set_callback (ct_autodetection->sync_source, sync_with_main_context,
|
||||||
|
ct_autodetection, NULL);
|
||||||
|
g_source_attach (ct_autodetection->sync_source, NULL);
|
||||||
|
|
||||||
|
while (!ct_autodetection->protocol_stopped &&
|
||||||
|
ct_autodetection->pending_net_char_sync)
|
||||||
|
{
|
||||||
|
g_cond_wait (&ct_autodetection->net_char_sync_cond,
|
||||||
|
&ct_autodetection->net_char_sync_mutex);
|
||||||
|
}
|
||||||
|
g_mutex_unlock (&ct_autodetection->net_char_sync_mutex);
|
||||||
|
}
|
||||||
|
|
||||||
|
static BOOL
|
||||||
|
autodetect_network_characteristics_sync (rdpAutoDetect *rdp_autodetect,
|
||||||
|
RDP_TRANSPORT_TYPE transport_type,
|
||||||
|
uint16_t sequenceNumber,
|
||||||
|
uint32_t bandwidth,
|
||||||
|
uint32_t rtt)
|
||||||
|
{
|
||||||
|
GrdRdpNetworkAutodetection *network_autodetection = rdp_autodetect->custom;
|
||||||
|
GrdRdpConnectTimeAutodetection *ct_autodetection =
|
||||||
|
grd_rdp_network_autodetection_get_ct_handler (network_autodetection);
|
||||||
|
g_autoptr (GMutexLocker) locker = NULL;
|
||||||
|
gboolean pending_sync = FALSE;
|
||||||
|
|
||||||
|
locker = g_mutex_locker_new (&ct_autodetection->ct_autodetection_mutex);
|
||||||
|
switch (ct_autodetection->state)
|
||||||
|
{
|
||||||
|
case CT_AUTODETECT_STATE_NONE:
|
||||||
|
case CT_AUTODETECT_STATE_MEASURE_BW_1:
|
||||||
|
case CT_AUTODETECT_STATE_MEASURE_BW_2:
|
||||||
|
case CT_AUTODETECT_STATE_MEASURE_BW_3:
|
||||||
|
g_assert_not_reached ();
|
||||||
|
break;
|
||||||
|
case CT_AUTODETECT_STATE_AWAIT_BW_RESULT_1:
|
||||||
|
case CT_AUTODETECT_STATE_AWAIT_BW_RESULT_2:
|
||||||
|
case CT_AUTODETECT_STATE_AWAIT_BW_RESULT_3:
|
||||||
|
g_assert (!pending_sync);
|
||||||
|
break;
|
||||||
|
case CT_AUTODETECT_STATE_START_RTT_DETECTION:
|
||||||
|
g_assert_not_reached ();
|
||||||
|
break;
|
||||||
|
case CT_AUTODETECT_STATE_AWAIT_LAST_RTT_RESPONSE:
|
||||||
|
pending_sync = TRUE;
|
||||||
|
break;
|
||||||
|
case CT_AUTODETECT_STATE_SEND_NET_CHAR_RESULT:
|
||||||
|
g_assert_not_reached ();
|
||||||
|
break;
|
||||||
|
case CT_AUTODETECT_STATE_COMPLETE:
|
||||||
|
g_warning ("[RDP] Connect-Time Autodetect: Received stray Network "
|
||||||
|
"Characteristics Sync PDU (current state: %s). Ignoring PDU...",
|
||||||
|
ct_autodetect_state_to_string (ct_autodetection->state));
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
g_clear_pointer (&locker, g_mutex_locker_free);
|
||||||
|
|
||||||
|
if (pending_sync)
|
||||||
|
sync_ct_autodetect_handling (ct_autodetection);
|
||||||
|
|
||||||
|
g_debug ("[RDP] Connect-Time Autodetect: Received Sync PDU: "
|
||||||
|
"RTT: %ums, bandwidth: %uKBit/s", rtt, bandwidth);
|
||||||
|
|
||||||
|
locker = g_mutex_locker_new (&ct_autodetection->ct_autodetection_mutex);
|
||||||
|
transition_to_state (ct_autodetection, CT_AUTODETECT_STATE_COMPLETE);
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static FREERDP_AUTODETECT_STATE
|
||||||
|
autodetect_on_connect_time_autodetect_progress (rdpAutoDetect *rdp_autodetect)
|
||||||
|
{
|
||||||
|
GrdRdpNetworkAutodetection *network_autodetection = rdp_autodetect->custom;
|
||||||
|
GrdRdpConnectTimeAutodetection *ct_autodetection =
|
||||||
|
grd_rdp_network_autodetection_get_ct_handler (network_autodetection);
|
||||||
|
|
||||||
|
return detect_network_characteristics (ct_autodetection);
|
||||||
|
}
|
||||||
|
|
||||||
|
GrdRdpConnectTimeAutodetection *
|
||||||
|
grd_rdp_connect_time_autodetection_new (GrdRdpNetworkAutodetection *network_autodetection,
|
||||||
|
rdpAutoDetect *rdp_autodetect)
|
||||||
|
{
|
||||||
|
GrdRdpConnectTimeAutodetection *ct_autodetection;
|
||||||
|
|
||||||
|
ct_autodetection = g_object_new (GRD_TYPE_RDP_CONNECT_TIME_AUTODETECTION, NULL);
|
||||||
|
ct_autodetection->network_autodetection = network_autodetection;
|
||||||
|
ct_autodetection->rdp_autodetect = rdp_autodetect;
|
||||||
|
|
||||||
|
rdp_autodetect->NetworkCharacteristicsSync = autodetect_network_characteristics_sync;
|
||||||
|
rdp_autodetect->OnConnectTimeAutoDetectProgress = autodetect_on_connect_time_autodetect_progress;
|
||||||
|
|
||||||
|
return ct_autodetection;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
grd_rdp_connect_time_autodetection_dispose (GObject *object)
|
||||||
|
{
|
||||||
|
GrdRdpConnectTimeAutodetection *ct_autodetection = GRD_RDP_CONNECT_TIME_AUTODETECTION (object);
|
||||||
|
|
||||||
|
g_assert (ct_autodetection->protocol_stopped);
|
||||||
|
|
||||||
|
if (ct_autodetection->sync_source)
|
||||||
|
{
|
||||||
|
g_source_destroy (ct_autodetection->sync_source);
|
||||||
|
g_clear_pointer (&ct_autodetection->sync_source, g_source_unref);
|
||||||
|
}
|
||||||
|
if (ct_autodetection->ping_source)
|
||||||
|
{
|
||||||
|
g_source_destroy (ct_autodetection->ping_source);
|
||||||
|
g_clear_pointer (&ct_autodetection->ping_source, g_source_unref);
|
||||||
|
}
|
||||||
|
|
||||||
|
G_OBJECT_CLASS (grd_rdp_connect_time_autodetection_parent_class)->dispose (object);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
grd_rdp_connect_time_autodetection_finalize (GObject *object)
|
||||||
|
{
|
||||||
|
GrdRdpConnectTimeAutodetection *ct_autodetection = GRD_RDP_CONNECT_TIME_AUTODETECTION (object);
|
||||||
|
|
||||||
|
g_cond_clear (&ct_autodetection->net_char_sync_cond);
|
||||||
|
g_mutex_clear (&ct_autodetection->net_char_sync_mutex);
|
||||||
|
g_mutex_clear (&ct_autodetection->ct_autodetection_mutex);
|
||||||
|
|
||||||
|
G_OBJECT_CLASS (grd_rdp_connect_time_autodetection_parent_class)->finalize (object);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
grd_rdp_connect_time_autodetection_init (GrdRdpConnectTimeAutodetection *ct_autodetection)
|
||||||
|
{
|
||||||
|
ct_autodetection->state = CT_AUTODETECT_STATE_NONE;
|
||||||
|
|
||||||
|
ct_autodetection->pending_pings = CT_N_PINGS;
|
||||||
|
ct_autodetection->pending_last_sequence_number = TRUE;
|
||||||
|
|
||||||
|
g_mutex_init (&ct_autodetection->ct_autodetection_mutex);
|
||||||
|
g_mutex_init (&ct_autodetection->net_char_sync_mutex);
|
||||||
|
g_cond_init (&ct_autodetection->net_char_sync_cond);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
grd_rdp_connect_time_autodetection_class_init (GrdRdpConnectTimeAutodetectionClass *klass)
|
||||||
|
{
|
||||||
|
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
||||||
|
|
||||||
|
object_class->dispose = grd_rdp_connect_time_autodetection_dispose;
|
||||||
|
object_class->finalize = grd_rdp_connect_time_autodetection_finalize;
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user