/pidgin/main: e4c122196b08: Add out-of-band DTMF support and dia...
David Woodhouse <[email protected]>
| Newsgroups | gmane.comp.gnome.gaim.cvs |
|---|---|
| Message-ID | <[email protected]> |
Changeset: e4c122196b08fa0596351ac000f5440128a13449 Author: David Woodhouse <[email protected]> Date: 2014-10-08 23:10 +0100 Branch: default URL: https://hg.pidgin.im/pidgin/main/rev/e4c122196b08 Description: Add out-of-band DTMF support and dialpad to use it This resolves the non-Jabber-specific part of ticket 12617, by adding purple_media_send_dtmf(), and a corresponding method to the PurpleMediaBackendIface and the farstream implementation thereof. Fixes #15575 Refs #12617 diffstat: libpurple/media.c | 43 ++++++++++++ libpurple/media.h | 20 +++++- libpurple/media/backend-fs2.c | 63 ++++++++++++++++++ libpurple/media/backend-iface.h | 3 + pidgin/gtkmedia.c | 137 +++++++++++++++++++++++++++++++++++++++- 5 files changed, 263 insertions(+), 3 deletions(-) diffs (truncated from 355 to 300 lines): diff --git a/libpurple/media.c b/libpurple/media.c --- a/libpurple/media.c +++ b/libpurple/media.c @@ -1420,3 +1420,46 @@ purple_media_get_tee(PurpleMedia *media, } #endif /* USE_GSTREAMER */ +gboolean +purple_media_send_dtmf(PurpleMedia *media, const gchar *session_id, + gchar dtmf, guint8 volume, guint16 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'; + + g_return_val_if_fail(strchr("0123456789ABCD#*", dtmf), FALSE); + + 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 --- 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(Pu */ 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, guint16 duration); + G_END_DECLS #endif /* _PURPLE_MEDIA_H_ */ diff --git a/libpurple/media/backend-fs2.c b/libpurple/media/backend-fs2.c --- a/libpurple/media/backend-fs2.c +++ b/libpurple/media/backend-fs2.c @@ -91,6 +91,9 @@ static gboolean purple_media_backend_fs2 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, guint16 duration); static void free_stream(PurpleMediaBackendFs2Stream *stream); static void free_session(PurpleMediaBackendFs2Session *session); @@ -531,6 +534,7 @@ purple_media_backend_iface_init(PurpleMe 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(Purp 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, + guint16 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 --- 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, + guint16 duration); }; /** diff --git a/pidgin/gtkmedia.c b/pidgin/gtkmedia.c --- 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(PidginMedi } 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 = _(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); + } +