besser82 / rpms / pidgin

Forked from rpms/pidgin 6 years ago
Clone
f86d088
f86d088
# HG changeset patch
f86d088
# User David Woodhouse <David.Woodhouse@intel.com>
f86d088
# Date 1425675783 0
f86d088
# Node ID 6b4576edf2a694ab55d0d06d3643c44601a75b15
f86d088
# Parent  714ba418d0aa5ba0cc4cc3b9db37296cd2bbf041
f86d088
Add out-of-band DTMF support and dialpad to use it
f86d088
f86d088
This is a backport of e4c122196b08 from the trunk. It adds the UI and
f86d088
farstream backend support for sending DTMF.
f86d088
f86d088
Fixes #15575
f86d088
f86d088
diff --git a/libpurple/media.c b/libpurple/media.c
f86d088
--- a/libpurple/media.c
f86d088
+++ b/libpurple/media.c
f86d088
@@ -1439,3 +1439,46 @@
f86d088
 }
f86d088
 #endif /* USE_GSTREAMER */
f86d088
 
f86d088
+gboolean
f86d088
+purple_media_send_dtmf(PurpleMedia *media, const gchar *session_id,
f86d088
+		gchar dtmf, guint8 volume, guint16 duration)
f86d088
+{
f86d088
+#ifdef USE_VV
f86d088
+	PurpleAccount *account = NULL;
f86d088
+	PurpleConnection *gc = NULL;
f86d088
+	PurplePlugin *prpl = NULL;
f86d088
+	PurplePluginProtocolInfo *prpl_info = NULL;
f86d088
+	PurpleMediaBackendIface *backend_iface = NULL;
f86d088
+
f86d088
+	if (media)
f86d088
+	{
f86d088
+		account = purple_media_get_account(media);
f86d088
+		backend_iface = PURPLE_MEDIA_BACKEND_GET_INTERFACE(media->priv->backend);
f86d088
+	}
f86d088
+	if (account)
f86d088
+		gc = purple_account_get_connection(account);
f86d088
+	if (gc)
f86d088
+		prpl = purple_connection_get_prpl(gc);
f86d088
+	if (prpl)
f86d088
+		prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl);
f86d088
+
f86d088
+	if (dtmf == 'a')
f86d088
+		dtmf = 'A';
f86d088
+	else if (dtmf == 'b')
f86d088
+		dtmf = 'B';
f86d088
+	else if (dtmf == 'c')
f86d088
+		dtmf = 'C';
f86d088
+	else if (dtmf == 'd')
f86d088
+		dtmf = 'D';
f86d088
+
f86d088
+	g_return_val_if_fail(strchr("0123456789ABCD#*", dtmf), FALSE);
f86d088
+
f86d088
+	if (backend_iface && backend_iface->send_dtmf
f86d088
+		&& backend_iface->send_dtmf(media->priv->backend,
f86d088
+				session_id, dtmf, volume, duration))
f86d088
+	{
f86d088
+		return TRUE;
f86d088
+	}
f86d088
+#endif
f86d088
+	return FALSE;
f86d088
+}
f86d088
diff --git a/libpurple/media.h b/libpurple/media.h
f86d088
--- a/libpurple/media.h
f86d088
+++ b/libpurple/media.h
f86d088
@@ -437,6 +437,21 @@
f86d088
  */
f86d088
 void purple_media_remove_output_windows(PurpleMedia *media);
f86d088
 
f86d088
+/**
f86d088
+ * Sends a DTMF signal out-of-band.
f86d088
+ *
f86d088
+ * @param media The media instance to send a DTMF signal to.
f86d088
+ * @param sess_id The session id of the session to send the DTMF signal on.
f86d088
+ * @param dtmf The character representing the DTMF in the range [0-9#*A-D].
f86d088
+ * @param volume The power level expressed in dBm0 after dropping the sign
f86d088
+ *      in the range of 0 to 63.  A larger value represents a lower volume.
f86d088
+ * @param duration The duration of the tone in milliseconds.
f86d088
+ *
f86d088
+ * @since 2.11
f86d088
+ */
f86d088
+gboolean purple_media_send_dtmf(PurpleMedia *media, const gchar *session_id,
f86d088
+		gchar dtmf, guint8 volume, guint16 duration);
f86d088
+
f86d088
 #ifdef __cplusplus
f86d088
 }
f86d088
 #endif
f86d088
diff --git a/libpurple/media/backend-fs2.c b/libpurple/media/backend-fs2.c
f86d088
--- a/libpurple/media/backend-fs2.c
f86d088
+++ b/libpurple/media/backend-fs2.c
f86d088
@@ -94,6 +94,9 @@
f86d088
 static void purple_media_backend_fs2_set_params(PurpleMediaBackend *self,
f86d088
 		guint num_params, GParameter *params);
f86d088
 static const gchar **purple_media_backend_fs2_get_available_params(void);
f86d088
+static gboolean purple_media_backend_fs2_send_dtmf(
f86d088
+		PurpleMediaBackend *self, const gchar *sess_id,
f86d088
+		gchar dtmf, guint8 volume, guint16 duration);
f86d088
 
f86d088
 static void free_stream(PurpleMediaBackendFs2Stream *stream);
f86d088
 static void free_session(PurpleMediaBackendFs2Session *session);
f86d088
@@ -499,6 +502,7 @@
f86d088
 	iface->set_send_codec = purple_media_backend_fs2_set_send_codec;
f86d088
 	iface->set_params = purple_media_backend_fs2_set_params;
f86d088
 	iface->get_available_params = purple_media_backend_fs2_get_available_params;
f86d088
+	iface->send_dtmf = purple_media_backend_fs2_send_dtmf;
f86d088
 }
f86d088
 
f86d088
 static FsMediaType
f86d088
@@ -2436,6 +2440,65 @@
f86d088
 
f86d088
 	return supported_params;
f86d088
 }
f86d088
+static gboolean
f86d088
+send_dtmf_callback(gpointer userdata)
f86d088
+{
f86d088
+	FsSession *session = userdata;
f86d088
+
f86d088
+	fs_session_stop_telephony_event(session);
f86d088
+
f86d088
+	return FALSE;
f86d088
+}
f86d088
+static gboolean
f86d088
+purple_media_backend_fs2_send_dtmf(PurpleMediaBackend *self,
f86d088
+		const gchar *sess_id, gchar dtmf, guint8 volume,
f86d088
+		guint16 duration)
f86d088
+{
f86d088
+	PurpleMediaBackendFs2Session *session;
f86d088
+	FsDTMFEvent event;
f86d088
+
f86d088
+	g_return_val_if_fail(PURPLE_IS_MEDIA_BACKEND_FS2(self), FALSE);
f86d088
+
f86d088
+	session = get_session(PURPLE_MEDIA_BACKEND_FS2(self), sess_id);
f86d088
+	if (session == NULL)
f86d088
+		return FALSE;
f86d088
+
f86d088
+	/* Convert DTMF char into FsDTMFEvent enum */
f86d088
+	switch(dtmf) {
f86d088
+		case '0': event = FS_DTMF_EVENT_0; break;
f86d088
+		case '1': event = FS_DTMF_EVENT_1; break;
f86d088
+		case '2': event = FS_DTMF_EVENT_2; break;
f86d088
+		case '3': event = FS_DTMF_EVENT_3; break;
f86d088
+		case '4': event = FS_DTMF_EVENT_4; break;
f86d088
+		case '5': event = FS_DTMF_EVENT_5; break;
f86d088
+		case '6': event = FS_DTMF_EVENT_6; break;
f86d088
+		case '7': event = FS_DTMF_EVENT_7; break;
f86d088
+		case '8': event = FS_DTMF_EVENT_8; break;
f86d088
+		case '9': event = FS_DTMF_EVENT_9; break;
f86d088
+		case '*': event = FS_DTMF_EVENT_STAR; break;
f86d088
+		case '#': event = FS_DTMF_EVENT_POUND; break;
f86d088
+		case 'A': event = FS_DTMF_EVENT_A; break;
f86d088
+		case 'B': event = FS_DTMF_EVENT_B; break;
f86d088
+		case 'C': event = FS_DTMF_EVENT_C; break;
f86d088
+		case 'D': event = FS_DTMF_EVENT_D; break;
f86d088
+		default:
f86d088
+			return FALSE;
f86d088
+	}
f86d088
+
f86d088
+	if (!fs_session_start_telephony_event(session->session,
f86d088
+			event, volume)) {
f86d088
+		return FALSE;
f86d088
+	}
f86d088
+
f86d088
+	if (duration <= 50) {
f86d088
+		fs_session_stop_telephony_event(session->session);
f86d088
+	} else {
f86d088
+		purple_timeout_add(duration, send_dtmf_callback,
f86d088
+				session->session);
f86d088
+	}
f86d088
+
f86d088
+	return TRUE;
f86d088
+}
f86d088
 #else
f86d088
 GType
f86d088
 purple_media_backend_fs2_get_type(void)
f86d088
diff --git a/libpurple/media/backend-iface.h b/libpurple/media/backend-iface.h
f86d088
--- a/libpurple/media/backend-iface.h
f86d088
+++ b/libpurple/media/backend-iface.h
f86d088
@@ -71,6 +71,9 @@
f86d088
 	void (*set_params) (PurpleMediaBackend *self,
f86d088
 		guint num_params, GParameter *params);
f86d088
 	const gchar **(*get_available_params) (void);
f86d088
+	gboolean (*send_dtmf) (PurpleMediaBackend *self,
f86d088
+		const gchar *sess_id, gchar dtmf, guint8 volume,
f86d088
+		guint16 duration);
f86d088
 };
f86d088
 
f86d088
 /**
f86d088
diff --git a/pidgin/gtkmedia.c b/pidgin/gtkmedia.c
f86d088
--- a/pidgin/gtkmedia.c
f86d088
+++ b/pidgin/gtkmedia.c
f86d088
@@ -41,6 +41,7 @@
f86d088
 #ifdef _WIN32
f86d088
 #include <gdk/gdkwin32.h>
f86d088
 #endif
f86d088
+#include <gdk/gdkkeysyms.h>
f86d088
 
f86d088
 #include <gst/interfaces/xoverlay.h>
f86d088
 
f86d088
@@ -759,6 +760,136 @@
f86d088
 }
f86d088
 
f86d088
 static void
f86d088
+phone_dtmf_pressed_cb(GtkButton *button, gpointer user_data)
f86d088
+{
f86d088
+	PidginMedia *gtkmedia = user_data;
f86d088
+	gint num;
f86d088
+	gchar *sid;
f86d088
+
f86d088
+	num = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(button), "dtmf-digit"));
f86d088
+	sid = g_object_get_data(G_OBJECT(button), "session-id");
f86d088
+
f86d088
+	purple_media_send_dtmf(gtkmedia->priv->media, sid, num, 25, 50);
f86d088
+}
f86d088
+
f86d088
+static inline GtkWidget *
f86d088
+phone_create_button(const gchar *text_hi, const gchar *text_lo)
f86d088
+{
f86d088
+	GtkWidget *button;
f86d088
+	GtkWidget *label_hi;
f86d088
+	GtkWidget *label_lo;
f86d088
+	GtkWidget *grid;
f86d088
+	const gchar *text_hi_local;
f86d088
+
f86d088
+	if (text_hi)
f86d088
+		text_hi_local = _(text_hi);
f86d088
+	else
f86d088
+		text_hi_local = "";
f86d088
+
f86d088
+	grid = gtk_vbox_new(TRUE, 0);
f86d088
+
f86d088
+	button = gtk_button_new();
f86d088
+	label_hi = gtk_label_new(text_hi_local);
f86d088
+	gtk_misc_set_alignment(GTK_MISC(label_hi), 0.5, 0.5);
f86d088
+	gtk_box_pack_end(GTK_BOX(grid), label_hi, FALSE, TRUE, 0);
f86d088
+	label_lo = gtk_label_new(text_lo);
f86d088
+	gtk_misc_set_alignment(GTK_MISC(label_lo), 0.5, 0.5);
f86d088
+	gtk_label_set_use_markup(GTK_LABEL(label_lo), TRUE);
f86d088
+	gtk_box_pack_end(GTK_BOX(grid), label_lo, FALSE, TRUE, 0);
f86d088
+	gtk_container_add(GTK_CONTAINER(button), grid);
f86d088
+
f86d088
+	return button;
f86d088
+}
f86d088
+
f86d088
+static struct phone_label {
f86d088
+	gchar *subtext;
f86d088
+	gchar *text;
f86d088
+	gchar chr;
f86d088
+} phone_labels[] = {
f86d088
+	{"1", NULL, '1'},
f86d088
+	/* Translators note: These are the letters on the keys of a numeric
f86d088
+	   keypad; translate according to ยง7.2.4 of
f86d088
+	 http://www.etsi.org/deliver/etsi_es/202100_202199/202130/01.01.01_60/es_20213 */
f86d088
+	 /* Letters on the '2' key of a numeric keypad */
f86d088
+	{"2", N_("ABC"), '2'},
f86d088
+	 /* Letters on the '3' key of a numeric keypad */
f86d088
+	{"3", N_("DEF"), '3'},
f86d088
+	 /* Letters on the '4' key of a numeric keypad */
f86d088
+	{"4", N_("GHI"), '4'},
f86d088
+	 /* Letters on the '5' key of a numeric keypad */
f86d088
+	{"5", N_("JKL"), '5'},
f86d088
+	 /* Letters on the '6' key of a numeric keypad */
f86d088
+	{"6", N_("MNO"), '6'},
f86d088
+	 /* Letters on the '7' key of a numeric keypad */
f86d088
+	{"7", N_("PQRS"), '7'},
f86d088
+	 /* Letters on the '8' key of a numeric keypad */
f86d088
+	{"8", N_("TUV"), '8'},
f86d088
+	 /* Letters on the '9' key of a numeric keypad */
f86d088
+	{"9", N_("WXYZ"), '9'},
f86d088
+	{"*", NULL, '*'},
f86d088
+	{"0", NULL, '0'},
f86d088
+	{"#", NULL, '#'},
f86d088
+	{NULL, NULL, 0}
f86d088
+};
f86d088
+
f86d088
+static gboolean
f86d088
+pidgin_media_dtmf_key_press_event_cb(GtkWidget *widget,
f86d088
+		GdkEvent *event, gpointer user_data)
f86d088
+{
f86d088
+	PidginMedia *gtkmedia = user_data;
f86d088
+	GdkEventKey *key = (GdkEventKey *) event;
f86d088
+
f86d088
+	if (event->type != GDK_KEY_PRESS) {
f86d088
+		return FALSE;
f86d088
+	}
f86d088
+
f86d088
+	if ((key->keyval >= GDK_KEY_0 && key->keyval <= GDK_KEY_9) ||
f86d088
+		key->keyval == GDK_KEY_asterisk ||
f86d088
+		key->keyval == GDK_KEY_numbersign) {
f86d088
+		gchar *sid = g_object_get_data(G_OBJECT(widget), "session-id");
f86d088
+
f86d088
+		purple_media_send_dtmf(gtkmedia->priv->media, sid, key->keyval, 25, 50);
f86d088
+	}
f86d088
+
f86d088
+	return FALSE;
f86d088
+}
f86d088
+
f86d088
+static GtkWidget *
f86d088
+pidgin_media_add_dtmf_widget(PidginMedia *gtkmedia,
f86d088
+		PurpleMediaSessionType type, const gchar *_sid)
f86d088
+{
f86d088
+	GtkWidget *grid = gtk_table_new(4, 3, TRUE);
f86d088
+	GtkWidget *button;
f86d088
+	gint index = 0;
f86d088
+	GtkWindow *win = &gtkmedia->parent;
f86d088
+
f86d088
+	/* Add buttons */
f86d088
+	for (index = 0; phone_labels[index].subtext != NULL; index++) {
f86d088
+		button = phone_create_button(phone_labels[index].text,
f86d088
+				phone_labels[index].subtext);
f86d088
+		g_signal_connect(button, "pressed",
f86d088
+				G_CALLBACK(phone_dtmf_pressed_cb), gtkmedia);
f86d088
+		g_object_set_data(G_OBJECT(button), "dtmf-digit",
f86d088
+				GINT_TO_POINTER(phone_labels[index].chr));
f86d088
+		g_object_set_data_full(G_OBJECT(button), "session-id",
f86d088
+				g_strdup(_sid), g_free);
f86d088
+		gtk_table_attach(GTK_TABLE(grid), button, index % 3,
f86d088
+				index % 3 + 1, index / 3, index / 3 + 1,
f86d088
+				GTK_FILL | GTK_EXPAND, GTK_FILL | GTK_EXPAND,
f86d088
+				2, 2);
f86d088
+	}
f86d088
+
f86d088
+	g_signal_connect(G_OBJECT(win), "key-press-event",
f86d088
+		G_CALLBACK(pidgin_media_dtmf_key_press_event_cb), gtkmedia);
f86d088
+	g_object_set_data_full(G_OBJECT(win), "session-id",
f86d088
+		g_strdup(_sid), g_free);
f86d088
+
f86d088
+	gtk_widget_show_all(grid);
f86d088
+
f86d088
+	return grid;
f86d088
+}
f86d088
+
f86d088
+static void
f86d088
 pidgin_media_ready_cb(PurpleMedia *media, PidginMedia *gtkmedia, const gchar *sid)
f86d088
 {
f86d088
 	GtkWidget *send_widget = NULL, *recv_widget = NULL, *button_widget = NULL;
f86d088
@@ -888,7 +1019,11 @@
f86d088
 
f86d088
 		gtk_box_pack_end(GTK_BOX(recv_widget),
f86d088
 				pidgin_media_add_audio_widget(gtkmedia,
f86d088
-				PURPLE_MEDIA_SEND_AUDIO, NULL), FALSE, FALSE, 0);
f86d088
+				PURPLE_MEDIA_SEND_AUDIO, sid), FALSE, FALSE, 0);
f86d088
+
f86d088
+		gtk_box_pack_end(GTK_BOX(recv_widget),
f86d088
+				pidgin_media_add_dtmf_widget(gtkmedia,
f86d088
+				PURPLE_MEDIA_SEND_AUDIO, sid), FALSE, FALSE, 0);
f86d088
 	}
f86d088
 
f86d088
 	if (type & PURPLE_MEDIA_AUDIO &&
f86d088