[PATCH v2] Add out-of-band DTMF support and dialpad to use it

David Woodhouse dwmw2 at infradead.org
Thu Mar 5 11:08:03 EST 2015


This is a combination of patches found in two tickets. Ticket 15575 had
a dialpad UI for sending DTMF within a voice call, but the
implementation of out-of-band DTMF support wasn't ideal. Ticket 12617
had more generic support for out-of-band DTMF, but no UI.

Ticket 12617 also added a Jabber-specific implementation, which I have
not included; it can go in easily as an additional patch once someone's
actually tested it. I've been testing SIP calls via Lync.

Fixes #15575
Refs #12617
---
v2: Add translations for the keytops.

 libpurple/media.c                        |  45 ++++++++++
 libpurple/media.h                        |  20 ++++-
 libpurple/media/backend-fs2.c            |  63 ++++++++++++++
 libpurple/media/backend-iface.h          |   3 +
 libpurple/protocols/bonjour/bonjour.c    |   3 +-
 libpurple/protocols/gg/gg.c              |   3 +-
 libpurple/protocols/irc/irc.c            |   3 +-
 libpurple/protocols/jabber/libfacebook.c |   3 +-
 libpurple/protocols/jabber/libgtalk.c    |   3 +-
 libpurple/protocols/jabber/libxmpp.c     |   3 +-
 libpurple/protocols/msn/msn.c            |   3 +-
 libpurple/protocols/mxit/mxit.c          |   3 +-
 libpurple/protocols/novell/novell.c      |   3 +-
 libpurple/protocols/null/nullprpl.c      |   3 +-
 libpurple/protocols/oscar/libaim.c       |   3 +-
 libpurple/protocols/oscar/libicq.c       |   3 +-
 libpurple/protocols/sametime/sametime.c  |   1 +
 libpurple/protocols/silc/silc.c          |   3 +-
 libpurple/protocols/simple/simple.c      |   3 +-
 libpurple/protocols/yahoo/libyahoo.c     |   3 +-
 libpurple/protocols/yahoo/libyahoojp.c   |   3 +-
 libpurple/protocols/zephyr/zephyr.c      |   3 +-
 libpurple/prpl.h                         |  10 +++
 pidgin/gtkmedia.c                        | 137 ++++++++++++++++++++++++++++++-
 24 files changed, 310 insertions(+), 20 deletions(-)

diff --git a/libpurple/media.c b/libpurple/media.c
index 631dbcb..34f542a 100644
--- a/libpurple/media.c
+++ b/libpurple/media.c
@@ -1420,3 +1420,48 @@ purple_media_get_tee(PurpleMedia *media,
 }
 #endif /* USE_GSTREAMER */
 
+gboolean
+purple_media_send_dtmf(PurpleMedia *media, const gchar *session_id,
+		gchar dtmf, guint8 volume, guint8 duration)
+{
+#ifdef USE_VV
+	PurpleAccount *account = NULL;
+	PurpleConnection *gc = NULL;
+	PurplePlugin *prpl = NULL;
+	PurplePluginProtocolInfo *prpl_info = NULL;
+	PurpleMediaBackendIface *backend_iface = NULL;
+
+	if (media)
+	{
+		account = purple_media_get_account(media);
+		backend_iface = PURPLE_MEDIA_BACKEND_GET_INTERFACE(media->priv->backend);
+	}
+	if (account)
+		gc = purple_account_get_connection(account);
+	if (gc)
+		prpl = purple_connection_get_prpl(gc);
+	if (prpl)
+		prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl);
+
+	if (dtmf == 'a')
+		dtmf = 'A';
+	else if (dtmf == 'b')
+		dtmf = 'B';
+	else if (dtmf == 'c')
+		dtmf = 'C';
+	else if (dtmf == 'd')
+		dtmf = 'D';
+
+	if (prpl_info && PURPLE_PROTOCOL_PLUGIN_HAS_FUNC(prpl_info, media_send_dtmf)
+		&& prpl_info->media_send_dtmf(media, dtmf, volume, duration))
+	{
+		return TRUE;
+	} else if (backend_iface && backend_iface->send_dtmf
+		&& backend_iface->send_dtmf(media->priv->backend,
+				session_id, dtmf, volume, duration))
+	{
+		return TRUE;
+	}
+#endif
+	return FALSE;
+}
diff --git a/libpurple/media.h b/libpurple/media.h
index 8578ba5..2b62334 100644
--- a/libpurple/media.h
+++ b/libpurple/media.h
@@ -31,6 +31,8 @@
 #include <glib.h>
 #include <glib-object.h>
 
+typedef struct _PurpleMedia PurpleMedia;
+
 #include "media/candidate.h"
 #include "media/codec.h"
 #include "media/enum-types.h"
@@ -42,8 +44,6 @@
 #define PURPLE_IS_MEDIA_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), PURPLE_TYPE_MEDIA))
 #define PURPLE_MEDIA_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS((obj), PURPLE_TYPE_MEDIA, PurpleMediaClass))
 
-typedef struct _PurpleMedia PurpleMedia;
-
 #include "signals.h"
 #include "util.h"
 
@@ -441,6 +441,22 @@ gulong purple_media_set_output_window(PurpleMedia *media,
  */
 void purple_media_remove_output_windows(PurpleMedia *media);
 
+/**
+ * purple_media_send_dtmf:
+ * @media: The media instance to send a DTMF signal to.
+ * @sess_id: The session id of the session to send the DTMF signal on.
+ * @dtmf: The character representing the DTMF in the range [0-9#*A-D].
+ * @volume: The power level expressed in dBm0 after dropping the sign in the
+ *          range of 0 to 63.  A larger value represents a lower volume.
+ * @duration: The duration of the tone in milliseconds.
+ *
+ * Sends a DTMF signal out-of-band.
+ *
+ * Returns: %TRUE DTMF sent successfully, or %FALSE otherwise.
+ */
+gboolean purple_media_send_dtmf(PurpleMedia *media, const gchar *session_id,
+		gchar dtmf, guint8 volume, guint8 duration);
+
 G_END_DECLS
 
 #endif  /* _PURPLE_MEDIA_H_ */
diff --git a/libpurple/media/backend-fs2.c b/libpurple/media/backend-fs2.c
index bd1050c..31ea97c 100644
--- a/libpurple/media/backend-fs2.c
+++ b/libpurple/media/backend-fs2.c
@@ -91,6 +91,9 @@ static gboolean purple_media_backend_fs2_set_send_codec(
 static void purple_media_backend_fs2_set_params(PurpleMediaBackend *self,
 		guint num_params, GParameter *params);
 static const gchar **purple_media_backend_fs2_get_available_params(void);
+static gboolean purple_media_backend_fs2_send_dtmf(
+		PurpleMediaBackend *self, const gchar *sess_id,
+		gchar dtmf, guint8 volume, guint8 duration);
 
 static void free_stream(PurpleMediaBackendFs2Stream *stream);
 static void free_session(PurpleMediaBackendFs2Session *session);
@@ -531,6 +534,7 @@ purple_media_backend_iface_init(PurpleMediaBackendIface *iface)
 	iface->set_send_codec = purple_media_backend_fs2_set_send_codec;
 	iface->set_params = purple_media_backend_fs2_set_params;
 	iface->get_available_params = purple_media_backend_fs2_get_available_params;
+	iface->send_dtmf = purple_media_backend_fs2_send_dtmf;
 }
 
 static FsMediaType
@@ -2557,6 +2561,65 @@ purple_media_backend_fs2_set_params(PurpleMediaBackend *self,
 	gst_structure_free(sdes);
 #endif /* HAVE_FARSIGHT */
 }
+static gboolean
+send_dtmf_callback(gpointer userdata)
+{
+	FsSession *session = userdata;
+
+	fs_session_stop_telephony_event(session);
+
+	return FALSE;
+}
+static gboolean
+purple_media_backend_fs2_send_dtmf(PurpleMediaBackend *self,
+		const gchar *sess_id, gchar dtmf, guint8 volume,
+		guint8 duration)
+{
+	PurpleMediaBackendFs2Session *session;
+	FsDTMFEvent event;
+
+	g_return_val_if_fail(PURPLE_IS_MEDIA_BACKEND_FS2(self), FALSE);
+
+	session = get_session(PURPLE_MEDIA_BACKEND_FS2(self), sess_id);
+	if (session == NULL)
+		return FALSE;
+
+	/* Convert DTMF char into FsDTMFEvent enum */
+	switch(dtmf) {
+		case '0': event = FS_DTMF_EVENT_0; break;
+		case '1': event = FS_DTMF_EVENT_1; break;
+		case '2': event = FS_DTMF_EVENT_2; break;
+		case '3': event = FS_DTMF_EVENT_3; break;
+		case '4': event = FS_DTMF_EVENT_4; break;
+		case '5': event = FS_DTMF_EVENT_5; break;
+		case '6': event = FS_DTMF_EVENT_6; break;
+		case '7': event = FS_DTMF_EVENT_7; break;
+		case '8': event = FS_DTMF_EVENT_8; break;
+		case '9': event = FS_DTMF_EVENT_9; break;
+		case '*': event = FS_DTMF_EVENT_STAR; break;
+		case '#': event = FS_DTMF_EVENT_POUND; break;
+		case 'A': event = FS_DTMF_EVENT_A; break;
+		case 'B': event = FS_DTMF_EVENT_B; break;
+		case 'C': event = FS_DTMF_EVENT_C; break;
+		case 'D': event = FS_DTMF_EVENT_D; break;
+		default:
+			return FALSE;
+	}
+
+	if (!fs_session_start_telephony_event(session->session,
+			event, volume)) {
+		return FALSE;
+	}
+
+	if (duration <= 50) {
+		fs_session_stop_telephony_event(session->session);
+	} else {
+		purple_timeout_add(duration, send_dtmf_callback,
+				session->session);
+	}
+
+	return TRUE;
+}
 #else
 GType
 purple_media_backend_fs2_get_type(void)
diff --git a/libpurple/media/backend-iface.h b/libpurple/media/backend-iface.h
index f6d2521..9ef245d 100644
--- a/libpurple/media/backend-iface.h
+++ b/libpurple/media/backend-iface.h
@@ -83,6 +83,9 @@ struct _PurpleMediaBackendIface
 	void (*set_params) (PurpleMediaBackend *self,
 		guint num_params, GParameter *params);
 	const gchar **(*get_available_params) (void);
+	gboolean (*send_dtmf) (PurpleMediaBackend *self,
+		const gchar *sess_id, gchar dtmf, guint8 volume,
+		guint8 duration);
 };
 
 /**
diff --git a/libpurple/protocols/bonjour/bonjour.c b/libpurple/protocols/bonjour/bonjour.c
index bc26c61..14943ad 100644
--- a/libpurple/protocols/bonjour/bonjour.c
+++ b/libpurple/protocols/bonjour/bonjour.c
@@ -562,7 +562,8 @@ static PurplePluginProtocolInfo prpl_info =
 	NULL,                                                    /* get_moods */
 	NULL,                                                    /* set_public_alias */
 	NULL,                                                    /* get_public_alias */
-	bonjour_get_max_message_size                             /* get_max_message_size */
+	bonjour_get_max_message_size,                            /* get_max_message_size */
+	NULL                                                     /* media_send_dtmf */
 };
 
 static PurplePluginInfo info =
diff --git a/libpurple/protocols/gg/gg.c b/libpurple/protocols/gg/gg.c
index 2a0e4da..1cb22e4 100644
--- a/libpurple/protocols/gg/gg.c
+++ b/libpurple/protocols/gg/gg.c
@@ -1015,7 +1015,8 @@ static PurplePluginProtocolInfo prpl_info =
 	NULL,				/* get_moods */
 	NULL,				/* set_public_alias */
 	NULL,				/* get_public_alias */
-	ggp_get_max_message_size	/* get_max_message_size */
+	ggp_get_max_message_size,	/* get_max_message_size */
+	NULL				/* media_send_dtmf */
 };
 
 static gboolean ggp_load(PurplePlugin *plugin);
diff --git a/libpurple/protocols/irc/irc.c b/libpurple/protocols/irc/irc.c
index ab4ef02..291a2d1 100644
--- a/libpurple/protocols/irc/irc.c
+++ b/libpurple/protocols/irc/irc.c
@@ -1005,7 +1005,8 @@ static PurplePluginProtocolInfo prpl_info =
 	NULL,					 /* get_moods */
 	NULL,					 /* set_public_alias */
 	NULL,					 /* get_public_alias */
-	irc_get_max_message_size		/* get_max_message_size */
+	irc_get_max_message_size,		/* get_max_message_size */
+	NULL					 /* media_send_dtmf */
 };
 
 static gboolean load_plugin (PurplePlugin *plugin) {
diff --git a/libpurple/protocols/jabber/libfacebook.c b/libpurple/protocols/jabber/libfacebook.c
index fbe9f7c..a719ed9 100644
--- a/libpurple/protocols/jabber/libfacebook.c
+++ b/libpurple/protocols/jabber/libfacebook.c
@@ -150,7 +150,8 @@ static PurplePluginProtocolInfo prpl_info =
 	NULL,							/* get_moods */
 	NULL, /* set_public_alias */
 	NULL, /* get_public_alias */
-	NULL  /* get_max_message_size */
+	NULL, /* get_max_message_size */
+	NULL  /* media_send_dtmf */
 };
 
 static gboolean load_plugin(PurplePlugin *plugin)
diff --git a/libpurple/protocols/jabber/libgtalk.c b/libpurple/protocols/jabber/libgtalk.c
index 8c53036..26a9262 100644
--- a/libpurple/protocols/jabber/libgtalk.c
+++ b/libpurple/protocols/jabber/libgtalk.c
@@ -133,7 +133,8 @@ static PurplePluginProtocolInfo prpl_info =
 	jabber_get_moods,  							/* get_moods */
 	NULL, /* set_public_alias */
 	NULL, /* get_public_alias */
-	NULL  /* get_max_message_size */
+	NULL, /* get_max_message_size */
+	NULL  /* media_send_dtmf */
 };
 
 static gboolean load_plugin(PurplePlugin *plugin)
diff --git a/libpurple/protocols/jabber/libxmpp.c b/libpurple/protocols/jabber/libxmpp.c
index c97bc80..5dc021a 100644
--- a/libpurple/protocols/jabber/libxmpp.c
+++ b/libpurple/protocols/jabber/libxmpp.c
@@ -127,7 +127,8 @@ static PurplePluginProtocolInfo prpl_info =
 	jabber_get_moods,  							/* get_moods */
 	NULL, /* set_public_alias */
 	NULL, /* get_public_alias */
-	NULL  /* get_max_message_size */
+	NULL, /* get_max_message_size */
+	NULL  /* media_send_dtmf */
 };
 
 static gboolean load_plugin(PurplePlugin *plugin)
diff --git a/libpurple/protocols/msn/msn.c b/libpurple/protocols/msn/msn.c
index a8fbe1e..c1284a0 100644
--- a/libpurple/protocols/msn/msn.c
+++ b/libpurple/protocols/msn/msn.c
@@ -2951,7 +2951,8 @@ static PurplePluginProtocolInfo prpl_info =
 	NULL,                               /* get_moods */
 	msn_set_public_alias,               /* set_public_alias */
 	msn_get_public_alias,               /* get_public_alias */
-	msn_get_max_message_size            /* get_max_message_size */
+	msn_get_max_message_size,           /* get_max_message_size */
+	NULL                                /* media_send_dtmf */
 };
 
 static PurplePluginInfo info =
diff --git a/libpurple/protocols/mxit/mxit.c b/libpurple/protocols/mxit/mxit.c
index c381110..f5ff33c 100644
--- a/libpurple/protocols/mxit/mxit.c
+++ b/libpurple/protocols/mxit/mxit.c
@@ -799,7 +799,8 @@ static PurplePluginProtocolInfo proto_info = {
 	mxit_get_moods,			/* get_moods */
 	NULL,					/* set_public_alias */
 	NULL,					/* get_public_alias */
-	NULL					/* get_max_message_size */
+	NULL,					/* get_max_message_size */
+	NULL					/* media_send_dtmf */
 };
 
 
diff --git a/libpurple/protocols/novell/novell.c b/libpurple/protocols/novell/novell.c
index cb031c7..0bf7523 100644
--- a/libpurple/protocols/novell/novell.c
+++ b/libpurple/protocols/novell/novell.c
@@ -3576,7 +3576,8 @@ static PurplePluginProtocolInfo prpl_info = {
 	NULL,						/* get_moods */
 	NULL,						/* set_public_alias */
 	NULL,						/* get_public_alias */
-	novell_get_max_message_size			/* get_max_message_size */
+	novell_get_max_message_size,			/* get_max_message_size */
+	NULL						/* media_send_dtmf */
 };
 
 static PurplePluginInfo info = {
diff --git a/libpurple/protocols/null/nullprpl.c b/libpurple/protocols/null/nullprpl.c
index bbb446a..f5cf653 100644
--- a/libpurple/protocols/null/nullprpl.c
+++ b/libpurple/protocols/null/nullprpl.c
@@ -1083,7 +1083,8 @@ static PurplePluginProtocolInfo prpl_info =
   NULL,                                /* get_moods */
   NULL,                                /* set_public_alias */
   NULL,                                /* get_public_alias */
-  NULL                                 /* get_max_message_size */
+  NULL,                                /* get_max_message_size */
+  NULL                                 /* media_send_dtmf */
 };
 
 static void nullprpl_init(PurplePlugin *plugin)
diff --git a/libpurple/protocols/oscar/libaim.c b/libpurple/protocols/oscar/libaim.c
index 6d65906..d44bdaf 100644
--- a/libpurple/protocols/oscar/libaim.c
+++ b/libpurple/protocols/oscar/libaim.c
@@ -99,7 +99,8 @@ static PurplePluginProtocolInfo prpl_info =
 	NULL,					/* get_moods */
 	NULL,					/* set_public_alias */
 	NULL,					/* get_public_alias */
-	oscar_get_max_message_size		/* get_max_message_size */
+	oscar_get_max_message_size,		/* get_max_message_size */
+	NULL					/* media_send_dtmf */
 };
 
 static PurplePluginInfo info =
diff --git a/libpurple/protocols/oscar/libicq.c b/libpurple/protocols/oscar/libicq.c
index b651747..cbb0676 100644
--- a/libpurple/protocols/oscar/libicq.c
+++ b/libpurple/protocols/oscar/libicq.c
@@ -115,7 +115,8 @@ static PurplePluginProtocolInfo prpl_info =
 	oscar_get_purple_moods, /* get_moods */
 	NULL,					/* set_public_alias */
 	NULL,					/* get_public_alias */
-	icq_get_max_message_size		/* get_max_message_size */
+	icq_get_max_message_size,		/* get_max_message_size */
+	NULL					/* media_send_dtmf */
 };
 
 static PurplePluginInfo info =
diff --git a/libpurple/protocols/sametime/sametime.c b/libpurple/protocols/sametime/sametime.c
index e2cb54c..5d4c6b7 100644
--- a/libpurple/protocols/sametime/sametime.c
+++ b/libpurple/protocols/sametime/sametime.c
@@ -5127,6 +5127,7 @@ static PurplePluginProtocolInfo mw_prpl_info = {
   NULL,
   NULL,
   NULL,
+  NULL,
   NULL
 };
 
diff --git a/libpurple/protocols/silc/silc.c b/libpurple/protocols/silc/silc.c
index b02a268..4e77586 100644
--- a/libpurple/protocols/silc/silc.c
+++ b/libpurple/protocols/silc/silc.c
@@ -2138,7 +2138,8 @@ static PurplePluginProtocolInfo prpl_info =
 	NULL,				        /* get_moods */
 	NULL,				        /* set_public_alias */
 	NULL,				        /* get_public_alias */
-	NULL					/* get_max_message_size */
+	NULL,					/* get_max_message_size */
+	NULL					/* media_send_dtmf */
 };
 
 static PurplePluginInfo info =
diff --git a/libpurple/protocols/simple/simple.c b/libpurple/protocols/simple/simple.c
index e1cba78..0a5a141 100644
--- a/libpurple/protocols/simple/simple.c
+++ b/libpurple/protocols/simple/simple.c
@@ -2119,7 +2119,8 @@ static PurplePluginProtocolInfo prpl_info =
 	NULL,					/* get_moods */
 	NULL,					/* set_public_alias */
 	NULL,					/* get_public_alias */
-	NULL					/* get_max_message_size */
+	NULL,					/* get_max_message_size */
+	NULL					/* media_send_dtmf */
 };
 
 
diff --git a/libpurple/protocols/yahoo/libyahoo.c b/libpurple/protocols/yahoo/libyahoo.c
index 9b9b5c5..8d6aa1e 100644
--- a/libpurple/protocols/yahoo/libyahoo.c
+++ b/libpurple/protocols/yahoo/libyahoo.c
@@ -264,7 +264,8 @@ static PurplePluginProtocolInfo prpl_info =
 	NULL,  /* get_moods */
 	NULL,  /* set_public_alias */
 	NULL,  /* get_public_alias */
-	yahoo_get_max_message_size
+	yahoo_get_max_message_size,
+	NULL  /* media_send_dtmf */
 };
 
 static PurplePluginInfo info =
diff --git a/libpurple/protocols/yahoo/libyahoojp.c b/libpurple/protocols/yahoo/libyahoojp.c
index b35bf12..a456b98 100644
--- a/libpurple/protocols/yahoo/libyahoojp.c
+++ b/libpurple/protocols/yahoo/libyahoojp.c
@@ -162,7 +162,8 @@ static PurplePluginProtocolInfo prpl_info =
 	NULL, /* get_moods */
 	NULL, /* set_public_alias */
 	NULL, /* get_public_alias */
-	yahoo_get_max_message_size
+	yahoo_get_max_message_size,
+	NULL  /* media_send_dtmf */
 };
 
 static PurplePluginInfo info =
diff --git a/libpurple/protocols/zephyr/zephyr.c b/libpurple/protocols/zephyr/zephyr.c
index 0598366..7bba4df 100644
--- a/libpurple/protocols/zephyr/zephyr.c
+++ b/libpurple/protocols/zephyr/zephyr.c
@@ -2959,7 +2959,8 @@ static PurplePluginProtocolInfo prpl_info = {
 	NULL,					/* get_moods */
 	NULL,					/* set_public_alias */
 	NULL,					/* get_public_alias */
-	NULL					/* get_max_message_size */
+	NULL,					/* get_max_message_size */
+	NULL					/* media_send_dtmf */
 };
 
 static PurplePluginInfo info = {
diff --git a/libpurple/prpl.h b/libpurple/prpl.h
index d96de33..1b26492 100644
--- a/libpurple/prpl.h
+++ b/libpurple/prpl.h
@@ -633,6 +633,16 @@ struct _PurplePluginProtocolInfo
 	 * Returns: Maximum message size, 0 if unspecified, -1 for infinite.
 	 */
 	gssize (*get_max_message_size)(PurpleConversation *conv);
+
+	/*
+	 * Sends DTMF codes out-of-band in a protocol-specific way if the
+	 * protocol supports it, or failing that in-band if the media backend
+	 * can do so.
+	 *
+	 * See purple_media_send_dtmf()
+	 */
+	gboolean (*media_send_dtmf)(PurpleMedia *media, gchar dtmf,
+				    guint8 volume, guint8 duration);
 };
 
 #define PURPLE_PROTOCOL_PLUGIN_HAS_FUNC(prpl, member) \
diff --git a/pidgin/gtkmedia.c b/pidgin/gtkmedia.c
index 9cac706..65147a6 100644
--- a/pidgin/gtkmedia.c
+++ b/pidgin/gtkmedia.c
@@ -43,6 +43,7 @@
 #ifdef GDK_WINDOWING_QUARTZ
 #include <gdk/gdkquartz.h>
 #endif
+#include <gdk/gdkkeysyms.h>
 
 #include "gtk3compat.h"
 
@@ -744,6 +745,136 @@ pidgin_media_add_audio_widget(PidginMedia *gtkmedia,
 }
 
 static void
+phone_dtmf_pressed_cb(GtkButton *button, gpointer user_data)
+{
+	PidginMedia *gtkmedia = user_data;
+	gint num;
+	gchar *sid;
+
+	num = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(button), "dtmf-digit"));
+	sid = g_object_get_data(G_OBJECT(button), "session-id");
+
+	purple_media_send_dtmf(gtkmedia->priv->media, sid, num, 25, 50);
+}
+
+static inline GtkWidget *
+phone_create_button(const gchar *text_hi, const gchar *text_lo)
+{
+	GtkWidget *button;
+	GtkWidget *label_hi;
+	GtkWidget *label_lo;
+	GtkWidget *grid;
+	gchar *text_hi_local;
+
+	if (text_hi)
+		text_hi_local = dgettext(PACKAGE, text_hi);
+	else
+		text_hi_local = "";
+
+	grid = gtk_vbox_new(TRUE, 0);
+
+	button = gtk_button_new();
+	label_hi = gtk_label_new(text_hi_local);
+	gtk_misc_set_alignment(GTK_MISC(label_hi), 0.5, 0.5);
+	gtk_box_pack_end(GTK_BOX(grid), label_hi, FALSE, TRUE, 0);
+	label_lo = gtk_label_new(text_lo);
+	gtk_misc_set_alignment(GTK_MISC(label_lo), 0.5, 0.5);
+	gtk_label_set_use_markup(GTK_LABEL(label_lo), TRUE);
+	gtk_box_pack_end(GTK_BOX(grid), label_lo, FALSE, TRUE, 0);
+	gtk_container_add(GTK_CONTAINER(button), grid);
+
+	return button;
+}
+
+static struct phone_label {
+	gchar *subtext;
+	gchar *text;
+	gchar chr;
+} phone_labels[] = {
+	{"<b>1</b>", NULL, '1'},
+	/* Translators note: These are the letters on the keys of a numeric
+	   keypad; translate according to §7.2.4 of
+	 http://www.etsi.org/deliver/etsi_es/202100_202199/202130/01.01.01_60/es_20213 */
+	 /* Letters on the '2' key of a numeric keypad */
+	{"<b>2</b>", N_("ABC"), '2'},
+	 /* Letters on the '3' key of a numeric keypad */
+	{"<b>3</b>", N_("DEF"), '3'},
+	 /* Letters on the '4' key of a numeric keypad */
+	{"<b>4</b>", N_("GHI"), '4'},
+	 /* Letters on the '5' key of a numeric keypad */
+	{"<b>5</b>", N_("JKL"), '5'},
+	 /* Letters on the '6' key of a numeric keypad */
+	{"<b>6</b>", N_("MNO"), '6'},
+	 /* Letters on the '7' key of a numeric keypad */
+	{"<b>7</b>", N_("PQRS"), '7'},
+	 /* Letters on the '8' key of a numeric keypad */
+	{"<b>8</b>", N_("TUV"), '8'},
+	 /* Letters on the '9' key of a numeric keypad */
+	{"<b>9</b>", N_("WXYZ"), '9'},
+	{"<b>*</b>", NULL, '*'},
+	{"<b>0</b>", NULL, '0'},
+	{"<b>#</b>", NULL, '#'},
+	{NULL, NULL, 0}
+};
+
+static gboolean
+pidgin_media_dtmf_key_press_event_cb(GtkWidget *widget,
+		GdkEvent *event, gpointer user_data)
+{
+	PidginMedia *gtkmedia = user_data;
+	GdkEventKey *key = (GdkEventKey *) event;
+
+	if (event->type != GDK_KEY_PRESS) {
+		return FALSE;
+	}
+
+	if ((key->keyval >= GDK_KEY_0 && key->keyval <= GDK_KEY_9) ||
+		key->keyval == GDK_KEY_asterisk ||
+		key->keyval == GDK_KEY_numbersign) {
+		gchar *sid = g_object_get_data(G_OBJECT(widget), "session-id");
+
+		purple_media_send_dtmf(gtkmedia->priv->media, sid, key->keyval, 25, 50);
+	}
+
+	return FALSE;
+}
+
+static GtkWidget *
+pidgin_media_add_dtmf_widget(PidginMedia *gtkmedia,
+		PurpleMediaSessionType type, const gchar *_sid)
+{
+	GtkWidget *grid = gtk_table_new(4, 3, TRUE);
+	GtkWidget *button;
+	gint index = 0;
+	GtkWindow *win = &gtkmedia->parent;
+
+	/* Add buttons */
+	for (index = 0; phone_labels[index].subtext != NULL; index++) {
+		button = phone_create_button(phone_labels[index].text,
+				phone_labels[index].subtext);
+		g_signal_connect(button, "pressed",
+				G_CALLBACK(phone_dtmf_pressed_cb), gtkmedia);
+		g_object_set_data(G_OBJECT(button), "dtmf-digit",
+				GINT_TO_POINTER(phone_labels[index].chr));
+		g_object_set_data_full(G_OBJECT(button), "session-id",
+				g_strdup(_sid), g_free);
+		gtk_table_attach(GTK_TABLE(grid), button, index % 3,
+				index % 3 + 1, index / 3, index / 3 + 1,
+				GTK_FILL | GTK_EXPAND, GTK_FILL | GTK_EXPAND,
+				2, 2);
+	}
+
+	g_signal_connect(G_OBJECT(win), "key-press-event",
+		G_CALLBACK(pidgin_media_dtmf_key_press_event_cb), gtkmedia);
+	g_object_set_data_full(G_OBJECT(win), "session-id",
+		g_strdup(_sid), g_free);
+
+	gtk_widget_show_all(grid);
+
+	return grid;
+}
+
+static void
 pidgin_media_ready_cb(PurpleMedia *media, PidginMedia *gtkmedia, const gchar *sid)
 {
 	GtkWidget *send_widget = NULL, *recv_widget = NULL, *button_widget = NULL;
@@ -889,7 +1020,11 @@ pidgin_media_ready_cb(PurpleMedia *media, PidginMedia *gtkmedia, const gchar *si
 
 		gtk_box_pack_end(GTK_BOX(recv_widget),
 				pidgin_media_add_audio_widget(gtkmedia,
-				PURPLE_MEDIA_SEND_AUDIO, NULL), FALSE, FALSE, 0);
+				PURPLE_MEDIA_SEND_AUDIO, sid), FALSE, FALSE, 0);
+
+		gtk_box_pack_end(GTK_BOX(recv_widget),
+				pidgin_media_add_dtmf_widget(gtkmedia,
+				PURPLE_MEDIA_SEND_AUDIO, sid), FALSE, FALSE, 0);
 	}
 
 	if (type & PURPLE_MEDIA_AUDIO &&
-- 
2.1.0


-- 
David Woodhouse                            Open Source Technology Centre
David.Woodhouse at intel.com                              Intel Corporation
-------------- next part --------------
A non-text attachment was scrubbed...
Name: smime.p7s
Type: application/x-pkcs7-signature
Size: 5745 bytes
Desc: not available
URL: <https://pidgin.im/pipermail/devel/attachments/20150305/cac8649d/attachment-0001.bin>


More information about the Devel mailing list