[RFC] Screen share window/monitor selection

David Woodhouse dwmw2 at infradead.org
Fri Apr 20 11:34:14 EDT 2018


This adds a method to PurpleRequestUiOps to request a
PurpleMediaElementInfo as a source for screen sharing.

The UI interacts with the user to select the window/monitor to be
shared, and the UI is responsible for providing the appropriate
GStreamer element.

The Pidgin side needs finishing — currently I'm using videotestsrc and
a nasty hack to test with ximagesrc, but actually I think I'm going to
end up with an appsrc which is fed with frames by
gdk_pixbuf_get_from_window() or something like that. Done properly,
that will work on Windows too.

For now I'm soliciting feedback on the basic approach and the libpurple
side of things.

This works on top of my patch in PR#322 which allows the PRPL to
specify a PurpleMediaElementInfo to use instead of the default for the
media type, by using g_object_set_data(media, "src-element", info)
before adding streams.


diff --git a/libpurple/request.c b/libpurple/request.c
index d61d241..da2b5ca 100644
--- a/libpurple/request.c
+++ b/libpurple/request.c
@@ -1501,6 +1501,31 @@ purple_request_folder(void *handle, const char *title, const char *dirname,
 	return NULL;
 }
 
+void *
+purple_request_screenshare_media(void *handle, const char *title,
+				 const char *primary, const char *secondary,
+				 PurpleAccount *account, GCallback cb,
+				 void *user_data)
+{
+	PurpleRequestUiOps *ops;
+
+	ops = purple_request_get_ui_ops();
+
+	if (ops != NULL && ops->request_screenshare_media != NULL) {
+		PurpleRequestInfo *info;
+
+		info            = g_new0(PurpleRequestInfo, 1);
+		info->type      = PURPLE_REQUEST_SCREENSHARE;
+		info->handle    = handle;
+		info->ui_handle = ops->request_screenshare_media(title, primary, secondary,
+								 account, cb, user_data);
+		handles = g_list_append(handles, info);
+		return info->ui_handle;
+	}
+
+	return NULL;
+}
+
 static void
 purple_request_close_info(PurpleRequestInfo *info)
 {
diff --git a/libpurple/request.h b/libpurple/request.h
index 8035ef8..282c909 100644
--- a/libpurple/request.h
+++ b/libpurple/request.h
@@ -47,7 +47,8 @@ typedef enum
 	PURPLE_REQUEST_ACTION,     /**< Action request.            */
 	PURPLE_REQUEST_FIELDS,     /**< Multiple fields request.   */
 	PURPLE_REQUEST_FILE,       /**< File open or save request. */
-	PURPLE_REQUEST_FOLDER      /**< Folder selection request.  */
+	PURPLE_REQUEST_FOLDER,     /**< Folder selection request.  */
+	PURPLE_REQUEST_SCREENSHARE /**< Screenshare media request. */
 
 } PurpleRequestType;
 
@@ -246,9 +247,12 @@ typedef struct
 	                        void *user_data,
 	                        size_t action_count, va_list actions);
 
+	void *(*request_screenshare_media)(const char *title, const char *primary,
+				const char *secondary, PurpleAccount *account,
+				GCallback cb, void *user_data);
+
 	void (*_purple_reserved1)(void);
 	void (*_purple_reserved2)(void);
-	void (*_purple_reserved3)(void);
 } PurpleRequestUiOps;
 
 typedef void (*PurpleRequestInputCb)(void *, const char *);
@@ -261,6 +265,7 @@ typedef void (*PurpleRequestActionCb)(void *, int);
 typedef void (*PurpleRequestChoiceCb)(void *, int);
 typedef void (*PurpleRequestFieldsCb)(void *, PurpleRequestFields *fields);
 typedef void (*PurpleRequestFileCb)(void *, const char *filename);
+typedef void (*PurpleRequestScreenshareCb)(void *, GObject *info);
 
 #ifdef __cplusplus
 extern "C" {
@@ -1576,6 +1581,30 @@ void *purple_request_folder(void *handle, const char *title, const char *dirname
 	PurpleAccount *account, const char *who, PurpleConversation *conv,
 	void *user_data);
 
+
+/**
+ * Displays a dialog allowing the user to select a window/monitor etc. for
+ * screen sharing. Returns a #PurpleMediaElementInfo to the callback or @c
+ * NULL if the request is cancelled.
+ *
+ * @param handle      The plugin or connection handle.  For some things this
+ *                    is <em>extremely</em> important.  See the comments on
+ *                    purple_request_input().
+ * @param title       The title of the message, or @c NULL if it should have
+ *                    no title.
+ * @param primary     The main point of the message, or @c NULL if you're
+ *                    feeling enigmatic.
+ * @param secondary   Secondary information, or @c NULL if there is none.
+ * @param cb          The callback for the @c OK button.
+ * @param user_data   The data to pass to the callback.
+ *
+ * @return A UI-specific handle.
+ */
+void *purple_request_screenshare_media(void *handle, const char *title,
+				       const char *primary, const char *secondary,
+				       PurpleAccount *account, GCallback cb,
+				       void *user_data);
+
 /*@}*/
 
 /**************************************************************************/
diff --git a/pidgin/gtkrequest.c b/pidgin/gtkrequest.c
index 397e09a..4b84df7 100644
--- a/pidgin/gtkrequest.c
+++ b/pidgin/gtkrequest.c
@@ -36,6 +36,9 @@
 #include "gtkutils.h"
 #include "pidginstock.h"
 #include "gtkblist.h"
+#ifdef USE_VV
+#include "media-gst.h"
+#endif
 
 #include <gdk/gdkkeysyms.h>
 
@@ -1703,6 +1706,157 @@ pidgin_request_folder(const char *title, const char *dirname,
 	return (void *)data;
 }
 
+#ifdef USE_VV
+static GstElement *create_videotestsrc_cb(PurpleMedia *media, const gchar *session_id,
+					  const gchar *participant)
+{
+	GstElement *ret;
+#if 1
+	XInitThreads(); /* Don't even ask */
+	ret = gst_element_factory_make("ximagesrc", NULL);
+	g_object_set(ret, "use-damage", 0, NULL);
+	//	g_object_set(ret, "xid", 0x1800438, NULL);
+	g_object_set(ret, "endx", 1000, "endy", 500, NULL);
+#else
+	ret = gst_element_factory_make("videotestsrc", NULL);
+#endif
+	return ret;
+}
+
+static void
+screenshare_videotestsrc_cb(GtkWidget *button, PidginRequestData *data)
+{
+	GObject *info;
+
+	generic_response_start(data);
+
+	if (!GTK_WIDGET_HAS_FOCUS(button))
+		gtk_widget_grab_focus(button);
+
+	if (data->cbs[0] != NULL) {
+		info = g_object_new(PURPLE_TYPE_MEDIA_ELEMENT_INFO,
+				    "id", "screenshare-videotessrc",
+				    "name", "Screen share test source",
+				    "type", PURPLE_MEDIA_ELEMENT_VIDEO | PURPLE_MEDIA_ELEMENT_SRC |
+				    PURPLE_MEDIA_ELEMENT_ONE_SRC,
+				    "create-cb", create_videotestsrc_cb, NULL);
+		((PurpleRequestScreenshareCb)data->cbs[0])(data->user_data, info);
+	}
+
+	purple_request_close(PURPLE_REQUEST_SCREENSHARE, data);
+}
+
+static void
+screenshare_cancel_cb(GtkWidget *button, PidginRequestData *data)
+{
+	generic_response_start(data);
+
+	if (data->cbs[0] != NULL)
+		((PurpleRequestScreenshareCb)data->cbs[0])(data->user_data, NULL);
+
+	purple_request_close(PURPLE_REQUEST_SCREENSHARE, data);
+}
+
+static gboolean
+destroy_screenshare_cb(GtkWidget *dialog, GdkEvent *event,
+		       PidginRequestData *data)
+{
+	screenshare_cancel_cb(NULL, data);
+	return FALSE;
+}
+
+static void *pidgin_request_screenshare_media(const char *title, const char *primary,
+					      const char *secondary, PurpleAccount *account,
+					      GCallback cb, void *user_data)
+{
+	PidginRequestData *data;
+	GtkWidget *dialog;
+	GtkWidget *vbox;
+	GtkWidget *hbox;
+	GtkWidget *label;
+	GtkWidget *button;
+	char *label_text;
+	char *primary_esc, *secondary_esc;
+
+	data            = g_new0(PidginRequestData, 1);
+	data->type      = PURPLE_REQUEST_SCREENSHARE;
+	data->user_data = user_data;
+
+	data->cb_count = 1;
+	data->cbs = g_new0(GCallback, 1);
+	data->cbs[0] = cb;
+
+	/* Create the dialog. */
+	data->dialog = dialog = gtk_dialog_new();
+
+	if (title != NULL)
+		gtk_window_set_title(GTK_WINDOW(dialog), title);
+#ifdef _WIN32
+	else
+		gtk_window_set_title(GTK_WINDOW(dialog), PIDGIN_ALERT_TITLE);
+#endif
+
+	button = pidgin_dialog_add_button(GTK_DIALOG(dialog), GTK_STOCK_CANCEL,
+					  G_CALLBACK(screenshare_cancel_cb), data);
+	GTK_WIDGET_SET_FLAGS(button, GTK_CAN_DEFAULT);
+
+	button = pidgin_dialog_add_button(GTK_DIALOG(dialog), _("Video test source"),
+					  G_CALLBACK(screenshare_videotestsrc_cb), data);
+	GTK_WIDGET_SET_FLAGS(button, GTK_CAN_DEFAULT);
+	gtk_window_set_default(GTK_WINDOW(dialog), button);
+
+	g_signal_connect(G_OBJECT(dialog), "delete_event",
+					 G_CALLBACK(destroy_screenshare_cb), data);
+
+	/* Setup the dialog */
+	gtk_container_set_border_width(GTK_CONTAINER(dialog), PIDGIN_HIG_BORDER/2);
+	gtk_container_set_border_width(GTK_CONTAINER(GTK_DIALOG(dialog)->vbox), PIDGIN_HIG_BORDER/2);
+	gtk_window_set_resizable(GTK_WINDOW(dialog), FALSE);
+	gtk_dialog_set_has_separator(GTK_DIALOG(dialog), FALSE);
+	gtk_box_set_spacing(GTK_BOX(GTK_DIALOG(dialog)->vbox), PIDGIN_HIG_BORDER);
+
+	/* Setup the main horizontal box */
+	hbox = gtk_hbox_new(FALSE, PIDGIN_HIG_BORDER);
+	gtk_container_add(GTK_CONTAINER(GTK_DIALOG(dialog)->vbox), hbox);
+
+
+	/* Vertical box */
+	vbox = gtk_vbox_new(FALSE, PIDGIN_HIG_BORDER);
+	gtk_box_pack_start(GTK_BOX(hbox), vbox, FALSE, FALSE, 0);
+
+	pidgin_widget_decorate_account(hbox, account);
+
+	/* Descriptive label */
+	primary_esc = (primary != NULL) ? g_markup_escape_text(primary, -1) : NULL;
+	secondary_esc = (secondary != NULL) ? g_markup_escape_text(secondary, -1) : NULL;
+	label_text = g_strdup_printf((primary ? "<span weight=\"bold\" size=\"larger\">"
+								 "%s</span>%s%s" : "%s%s%s"),
+								 (primary ? primary_esc : ""),
+								 ((primary && secondary) ? "\n\n" : ""),
+								 (secondary ? secondary_esc : ""));
+	g_free(primary_esc);
+	g_free(secondary_esc);
+
+	label = gtk_label_new(NULL);
+
+	gtk_label_set_markup(GTK_LABEL(label), label_text);
+	gtk_label_set_line_wrap(GTK_LABEL(label), TRUE);
+	gtk_misc_set_alignment(GTK_MISC(label), 0, 0);
+	gtk_label_set_selectable(GTK_LABEL(label), TRUE);
+	gtk_box_pack_start(GTK_BOX(vbox), label, TRUE, TRUE, 0);
+
+	g_free(label_text);
+
+	/* Show everything. */
+	pidgin_auto_parent_window(dialog);
+
+	gtk_widget_show_all(dialog);
+
+	return data;
+
+}
+#endif /* USE_VV */
+
 static void
 pidgin_close_request(PurpleRequestType type, void *ui_handle)
 {
@@ -1730,7 +1884,11 @@ static PurpleRequestUiOps ops =
 	pidgin_close_request,
 	pidgin_request_folder,
 	pidgin_request_action_with_icon,
+#ifdef USE_VV
+	pidgin_request_screenshare_media,
+#else
 	NULL,
+#endif
 	NULL,
 	NULL
 };
-------------- next part --------------
A non-text attachment was scrubbed...
Name: smime.p7s
Type: application/x-pkcs7-signature
Size: 5213 bytes
Desc: not available
URL: <https://pidgin.im/pipermail/devel/attachments/20180420/2bfd1341/attachment-0001.bin>


More information about the Devel mailing list