commit ba7c65188a8adcaa8d809c81517108ad62f1b245
parent 73095c3fcfd59636303d0de859f0fad003a5cd80
Author: Jacki <jacki@thejackimonster.de>
Date: Tue, 13 Aug 2024 17:47:59 +0200
Adjust code for handling screencast via async request (WIP)
Signed-off-by: Jacki <jacki@thejackimonster.de>
Diffstat:
9 files changed, 373 insertions(+), 74 deletions(-)
diff --git a/src/application.c b/src/application.c
@@ -218,6 +218,7 @@ application_init(MESSENGER_Application *app,
#ifndef MESSENGER_APPLICATION_NO_PORTAL
GError *error = NULL;
app->portal = xdp_portal_initable_new(&error);
+ app->session = NULL;
if (error)
{
@@ -412,6 +413,50 @@ application_pw_main_loop_run(MESSENGER_Application *app)
pw_main_loop_run(app->pw.main_loop);
}
+#ifndef MESSENGER_APPLICATION_NO_PORTAL
+void
+application_set_active_session(MESSENGER_Application *app,
+ XdpSession *session)
+{
+ g_assert(app);
+
+ if (app->session == session)
+ return;
+
+ if (app->session)
+ {
+ const XdpSessionState state = xdp_session_get_session_state(
+ app->session
+ );
+
+ if (XDP_SESSION_ACTIVE == state)
+ xdp_session_close(app->session);
+
+ g_object_unref(app->session);
+ }
+
+ app->session = session;
+}
+
+int
+application_get_active_session_remote(MESSENGER_Application *app)
+{
+ g_assert(app);
+
+ if (!(app->session))
+ return -1;
+
+ const XdpSessionState state = xdp_session_get_session_state(
+ app->session
+ );
+
+ if (XDP_SESSION_ACTIVE != state)
+ return -1;
+
+ return xdp_session_open_pipewire_remote(app->session);
+}
+#endif
+
static void
_request_background_callback(MESSENGER_Application *app,
gboolean success,
@@ -651,6 +696,8 @@ application_exit(MESSENGER_Application *app,
}
#ifndef MESSENGER_APPLICATION_NO_PORTAL
+ application_set_active_session(app, NULL);
+
if (app->portal)
g_object_unref(app->portal);
diff --git a/src/application.h b/src/application.h
@@ -94,6 +94,7 @@ typedef struct MESSENGER_Application
#ifndef MESSENGER_APPLICATION_NO_PORTAL
XdpPortal *portal;
XdpParent *parent;
+ XdpSession *session;
#endif
struct {
@@ -235,6 +236,32 @@ application_pw_core_cleanup(MESSENGER_Application *app);
void
application_pw_main_loop_run(MESSENGER_Application *app);
+#ifndef MESSENGER_APPLICATION_NO_PORTAL
+/**
+ * Sets the active session for the messenger
+ * application and frees the previous one
+ * if any is still active.
+ *
+ * @param app Messenger application
+ * @param session Screencast session
+ */
+void
+application_set_active_session(MESSENGER_Application *app,
+ XdpSession *session);
+
+/**
+ * Returns the file descriptor to the pipewire
+ * remote where the screencast streams of the
+ * active session from the messenger application
+ * are available.
+ *
+ * @param app Messenger application
+ * @return File descriptor
+ */
+int
+application_get_active_session_remote(MESSENGER_Application *app);
+#endif
+
/**
* Shows the messenger application main window.
*
diff --git a/src/discourse.c b/src/discourse.c
@@ -861,7 +861,7 @@ discourse_has_controls(struct GNUNET_CHAT_Discourse *discourse,
case MESSENGER_DISCOURSE_CTRL_WEBCAM:
return (info->video_record_pipeline? TRUE : FALSE);
case MESSENGER_DISCOURSE_CTRL_SCREEN_CAPTURE:
- return FALSE;
+ return (info->video_record_pipeline? TRUE : FALSE);
default:
return FALSE;
}
diff --git a/src/media.c b/src/media.c
@@ -179,64 +179,18 @@ media_init_camera_capturing(MESSENGER_MediaInfo *media,
media_pw_init(media, app, fd);
}
-#ifndef MESSENGER_APPLICATION_NO_PORTAL
-static void
-_request_screencast_callback(GObject *source_object,
- GAsyncResult *result,
- gpointer user_data)
-{
- g_assert((source_object) && (result) && (user_data));
-
- XdpPortal *portal = (XdpPortal*) source_object;
- MESSENGER_MediaInfo *media = (MESSENGER_MediaInfo*) user_data;
-
- GError *error = NULL;
- XdpSession *session = xdp_portal_create_screencast_session_finish(
- portal,
- result,
- &error
- );
-
- media->session = session;
-
- if (error)
- {
- g_printerr("ERROR: %s\n", error->message);
- g_error_free(error);
- }
- else
- {
- const int fd = xdp_session_open_pipewire_remote(media->session);
-
- media_pw_init(media, media->app, fd);
- }
-}
-#endif
-
void
media_init_screen_sharing(MESSENGER_MediaInfo *media,
MESSENGER_Application *app)
{
g_assert((media) && (app));
-
- media->app = app;
+ int fd = -1;
#ifndef MESSENGER_APPLICATION_NO_PORTAL
- if (app->portal)
- xdp_portal_create_screencast_session(
- app->portal,
- XDP_OUTPUT_MONITOR | XDP_OUTPUT_WINDOW,
- XDP_SCREENCAST_FLAG_NONE,
- XDP_CURSOR_MODE_EMBEDDED,
- XDP_PERSIST_MODE_NONE,
- NULL,
- NULL,
- _request_screencast_callback,
- media
- );
- else
+ fd = application_get_active_session_remote(app);
#endif
- media_pw_init(media, app, -1);
+
+ media_pw_init(media, app, fd);
}
static int
@@ -269,14 +223,6 @@ media_pw_cleanup(MESSENGER_MediaInfo *media)
pw_core_disconnect(media->pw.core);
media->pw.core = NULL;
-
-#ifndef MESSENGER_APPLICATION_NO_PORTAL
- if (media->session)
- xdp_session_close(media->session);
-
- media->session = NULL;
-#endif
-
media->app = NULL;
}
@@ -318,7 +264,8 @@ iterate_global(void *obj,
const char *name = NULL;
const char *description = NULL;
- const char *role = NULL;
+ const char *media_role = NULL;
+ const char *media_class = NULL;
const struct spa_dict_item *item;
spa_dict_for_each(item, props)
@@ -329,14 +276,24 @@ iterate_global(void *obj,
if (0 == g_strcmp0(item->key, "node.description"))
description = item->value;
+ if (0 == g_strcmp0(item->key, "media.class"))
+ media_class = item->value;
+
if (0 == g_strcmp0(item->key, "media.role"))
- role = item->value;
+ media_role = item->value;
}
- if ((!name) || (!description) || (!role))
+ if ((!name) || (!media_class))
return 0;
- closure->iterator(closure->cls, name, description, role);
+ closure->iterator(
+ closure->cls,
+ name,
+ description,
+ media_class,
+ media_role
+ );
+
return 0;
}
diff --git a/src/media.h b/src/media.h
@@ -25,10 +25,6 @@
#ifndef MEDIA_H_
#define MEDIA_H_
-#ifndef MESSENGER_APPLICATION_NO_PORTAL
-#include <libportal/portal.h>
-#endif
-
#include <pipewire/pipewire.h>
typedef struct MESSENGER_Application MESSENGER_Application;
@@ -36,10 +32,6 @@ typedef struct MESSENGER_Application MESSENGER_Application;
typedef struct MESSENGER_MediaInfo
{
MESSENGER_Application *app;
-
-#ifndef MESSENGER_APPLICATION_NO_PORTAL
- XdpSession *session;
-#endif
struct {
struct pw_core *core;
@@ -55,7 +47,8 @@ typedef void
(*MESSENGER_MediaNodeIterator) (void *cls,
const char *name,
const char *description,
- const char *role);
+ const char *media_class,
+ const char *media_role);
/**
* Initialize a media info structure to list
diff --git a/src/request.c b/src/request.c
@@ -24,6 +24,10 @@
#include "request.h"
+#ifndef MESSENGER_APPLICATION_NO_PORTAL
+#include <libportal/portal.h>
+#endif
+
#ifdef MESSENGER_APPLICATION_NO_PORTAL
static gboolean
@@ -224,6 +228,151 @@ request_new_camera(MESSENGER_Application *application,
return request;
}
+#ifndef MESSENGER_APPLICATION_NO_PORTAL
+static void
+_request_session_start_callback(GObject *source_object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ g_assert((source_object) && (result) && (user_data));
+
+ XdpSession *session = (XdpSession*) source_object;
+ MESSENGER_Request *request = (MESSENGER_Request*) user_data;
+
+ request_cleanup(request);
+
+ MESSENGER_Application *app = request->application;
+ MESSENGER_RequestCallback callback = request->callback;
+ gpointer data = request->user_data;
+
+ GError *error = NULL;
+ gboolean success = xdp_session_start_finish(
+ session,
+ result,
+ &error
+ );
+
+ application_set_active_session(app, success? session : NULL);
+ request_drop(request);
+
+ gboolean error_value = false;
+ if (error)
+ {
+ g_printerr("ERROR: %s\n", error->message);
+ g_error_free(error);
+
+ error_value = true;
+ }
+
+ if (callback)
+ callback(app, success, error_value, data);
+}
+
+static void
+_request_screencast_callback(GObject *source_object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ g_assert((source_object) && (result) && (user_data));
+
+ XdpPortal *portal = (XdpPortal*) source_object;
+ MESSENGER_Request *request = (MESSENGER_Request*) user_data;
+
+ request_cleanup(request);
+
+ MESSENGER_Application *app = request->application;
+ MESSENGER_RequestCallback callback = request->callback;
+ gpointer data = request->user_data;
+
+ GError *error = NULL;
+ XdpSession *session = xdp_portal_create_screencast_session_finish(
+ portal, result, &error
+ );
+
+ if (session)
+ application_set_active_session(app, session);
+ request_drop(request);
+
+ gboolean error_value = false;
+ if (error)
+ {
+ g_printerr("ERROR: %s\n", error->message);
+ g_error_free(error);
+
+ error_value = true;
+ }
+
+ if (!session)
+ goto skip_session_start;
+
+ GCancellable* cancellable = g_cancellable_new();
+
+ if (!cancellable)
+ goto skip_session_start;
+
+ request = request_new(
+ app,
+ callback,
+ cancellable,
+ user_data
+ );
+
+ xdp_session_start(
+ session,
+ app->parent,
+ cancellable,
+ _request_session_start_callback,
+ request
+ );
+
+ return;
+
+skip_session_start:
+ if (callback)
+ callback(app, false, error_value, data);
+}
+#endif
+
+MESSENGER_Request*
+request_new_screencast(MESSENGER_Application *application,
+ XdpOutputType outputs,
+ XdpScreencastFlags flags,
+ XdpCursorMode cursor_mode,
+ XdpPersistMode persist_mode,
+ MESSENGER_RequestCallback callback,
+ gpointer user_data)
+{
+ g_assert((application) && (callback));
+
+ GCancellable* cancellable = g_cancellable_new();
+
+ if (!cancellable)
+ return NULL;
+
+ MESSENGER_Request* request = request_new(
+ application,
+ callback,
+ cancellable,
+ user_data
+ );
+
+#ifndef MESSENGER_APPLICATION_NO_PORTAL
+ xdp_portal_create_screencast_session(
+ application->portal,
+ outputs,
+ flags,
+ cursor_mode,
+ persist_mode,
+ NULL,
+ cancellable,
+ _request_screencast_callback,
+ request
+ );
+#endif
+
+ return request;
+}
+
void
request_cancel(MESSENGER_Request *request)
{
diff --git a/src/request.h b/src/request.h
@@ -39,6 +39,30 @@ typedef enum XdpBackgroundFlags {
typedef enum XdpCameraFlags {
XDP_CAMERA_FLAG_NONE = 0,
} XdpCameraFlags;
+
+typedef enum XdpOutputType {
+ XDP_OUTPUT_NONE = 0,
+ XDP_OUTPUT_MONITOR = 1,
+ XDP_OUTPUT_WINDOW = 2,
+ XDP_OUTPUT_VIRTUAL = 4,
+} XdpOutputType;
+
+typedef enum XdpScreencastFlags {
+ XDP_SCREENCAST_FLAG_NONE = 0,
+ XDP_SCREENCAST_FLAG_MULTIPLE = 1,
+} XdpScreencastFlags;
+
+typedef enum XdpCursorMode {
+ XDP_CURSOR_MODE_HIDDEN = 1,
+ XDP_CURSOR_MODE_EMBEDDED = 2,
+ XDP_CURSOR_MODE_METADATA = 4,
+} XdpCursorMode;
+
+typedef enum XdpPersistMode {
+ XDP_PERSIST_MODE_NONE = 0,
+ XDP_PERSIST_MODE_TRANSIENT = 1,
+ XDP_PERSIST_MODE_PERSISTENT = 2,
+} XdpPersistMode;
#endif
#include "application.h"
@@ -112,6 +136,28 @@ request_new_camera(MESSENGER_Application *application,
gpointer user_data);
/**
+ * Creates a new request for the messsenger
+ * application for a screencast permission.
+ *
+ * @param application Messenger application
+ * @param outputs Output types
+ * @param flags Screencast flags
+ * @param cursor_mode Cursor mode
+ * @param persist_mode Persist mode
+ * @param callback Callback
+ * @param user_data User data
+ * @return New camera request object
+ */
+MESSENGER_Request*
+request_new_screencast(MESSENGER_Application *application,
+ XdpOutputType outputs,
+ XdpScreencastFlags flags,
+ XdpCursorMode cursor_mode,
+ XdpPersistMode persist_mode,
+ MESSENGER_RequestCallback callback,
+ gpointer user_data);
+
+/**
* Cancel a request object if possible.
*
* @param request Request object
diff --git a/src/ui/discourse.c b/src/ui/discourse.c
@@ -34,6 +34,7 @@
#include "../application.h"
#include "../discourse.h"
+#include "../request.h"
#include "../ui.h"
#include "../util.h"
@@ -123,6 +124,72 @@ handle_camera_button_click(UNUSED GtkButton *button,
}
static void
+iterate_streams(void *cls,
+ const char *name,
+ const char *description,
+ const char *media_class,
+ const char *media_role)
+{
+ g_assert(cls);
+
+ UI_DISCOURSE_Handle *handle = (UI_DISCOURSE_Handle*) cls;
+
+ if ((!name) || (!description) || (!media_class) || (!media_role))
+ return;
+
+ if (0 != g_strcmp0(media_class, "Stream/Output/Video"))
+ return;
+
+ // TODO
+}
+
+static void
+_request_screen_callback(MESSENGER_Application *app,
+ gboolean success,
+ gboolean error,
+ gpointer user_data)
+{
+ g_assert((app) && (user_data));
+
+ UI_DISCOURSE_Handle *handle = (UI_DISCOURSE_Handle*) user_data;
+
+#ifndef MESSENGER_APPLICATION_NO_PORTAL
+ if ((app->portal) && (success))
+#else
+ if (success)
+#endif
+ {
+ media_init_screen_sharing(&(app->media.screen), app);
+ media_pw_main_loop_run(&(app->media.screen));
+
+ media_pw_iterate_nodes(&(app->media.screen), iterate_streams, handle);
+
+ // TODO
+ }
+}
+
+static void
+handle_screen_button_click(UNUSED GtkButton *button,
+ gpointer user_data)
+{
+ g_assert(user_data);
+
+ UI_DISCOURSE_Handle *handle = (UI_DISCOURSE_Handle*) user_data;
+
+ // TODO
+
+ request_new_screencast(
+ handle->app,
+ XDP_OUTPUT_MONITOR | XDP_OUTPUT_WINDOW,
+ XDP_SCREENCAST_FLAG_NONE,
+ XDP_CURSOR_MODE_EMBEDDED,
+ XDP_PERSIST_MODE_TRANSIENT,
+ _request_screen_callback,
+ handle
+ );
+}
+
+static void
handle_speakers_button_value_changed(UNUSED GtkScaleButton *button,
gdouble value,
gpointer user_data)
@@ -334,6 +401,13 @@ ui_discourse_window_init(MESSENGER_Application *app,
);
g_signal_connect(
+ handle->screen_button,
+ "clicked",
+ G_CALLBACK(handle_screen_button_click),
+ handle
+ );
+
+ g_signal_connect(
handle->speakers_button,
"value-changed",
G_CALLBACK(handle_speakers_button_value_changed),
diff --git a/src/ui/new_contact.c b/src/ui/new_contact.c
@@ -328,13 +328,19 @@ static void
iterate_cameras(void *cls,
const char *name,
const char *description,
- const char *role)
+ const char *media_class,
+ const char *media_role)
{
g_assert(cls);
UI_NEW_CONTACT_Handle *handle = (UI_NEW_CONTACT_Handle*) cls;
- if (0 != g_strcmp0(role, "Camera"))
+ if ((!name) || (!description) || (!media_class) || (!media_role))
+ return;
+
+ if (0 != g_strcmp0(media_class, "Video/Source"))
+ return;
+ if (0 != g_strcmp0(media_role, "Camera"))
return;
GtkTreeIter iter;