mirror of
https://github.com/morgan9e/grd
synced 2026-04-13 16:04:13 +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