Merged in default (pull request #594)
[pidgin-git.git] / pidgin / gtkmedia.c
blob9caf5dabc46c2680a3fea67b66b0b60785bec77d
1 /* pidgin
3 * Pidgin is the legal property of its developers, whose names are too numerous
4 * to list here. Please refer to the COPYRIGHT file distributed with this
5 * source distribution.
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
22 #include "internal.h"
23 #include "debug.h"
24 #include "connection.h"
25 #include "media.h"
26 #include "mediamanager.h"
27 #include "pidgin.h"
28 #include "request.h"
30 #include "gtkmedia.h"
31 #include "gtkutils.h"
32 #include "pidginstock.h"
34 #ifdef USE_VV
35 #include "media-gst.h"
37 #ifdef GDK_WINDOWING_WIN32
38 #include <gdk/gdkwin32.h>
39 #endif
40 #ifdef GDK_WINDOWING_X11
41 #include <gdk/gdkx.h>
42 #endif
43 #ifdef GDK_WINDOWING_QUARTZ
44 #include <gdk/gdkquartz.h>
45 #endif
46 #include <gdk/gdkkeysyms.h>
48 #include "gtk3compat.h"
50 #define PIDGIN_TYPE_MEDIA (pidgin_media_get_type())
51 #define PIDGIN_MEDIA(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), PIDGIN_TYPE_MEDIA, PidginMedia))
52 #define PIDGIN_MEDIA_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), PIDGIN_TYPE_MEDIA, PidginMediaClass))
53 #define PIDGIN_IS_MEDIA(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), PIDGIN_TYPE_MEDIA))
54 #define PIDGIN_IS_MEDIA_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), PIDGIN_TYPE_MEDIA))
55 #define PIDGIN_MEDIA_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), PIDGIN_TYPE_MEDIA, PidginMediaClass))
57 typedef struct _PidginMedia PidginMedia;
58 typedef struct _PidginMediaClass PidginMediaClass;
59 typedef struct _PidginMediaPrivate PidginMediaPrivate;
61 typedef enum
63 /* Waiting for response */
64 PIDGIN_MEDIA_WAITING = 1,
65 /* Got request */
66 PIDGIN_MEDIA_REQUESTED,
67 /* Accepted call */
68 PIDGIN_MEDIA_ACCEPTED,
69 /* Rejected call */
70 PIDGIN_MEDIA_REJECTED,
71 } PidginMediaState;
73 struct _PidginMediaClass
75 GtkApplicationWindowClass parent_class;
78 struct _PidginMedia
80 GtkApplicationWindow parent;
81 PidginMediaPrivate *priv;
84 struct _PidginMediaPrivate
86 PurpleMedia *media;
87 gchar *screenname;
88 gulong level_handler_id;
90 GtkBuilder *ui;
91 GtkWidget *menubar;
92 GtkWidget *statusbar;
94 GtkWidget *hold;
95 GtkWidget *mute;
96 GtkWidget *pause;
98 GtkWidget *send_progress;
99 GHashTable *recv_progressbars;
101 PidginMediaState state;
103 GtkWidget *display;
104 GtkWidget *send_widget;
105 GtkWidget *recv_widget;
106 GtkWidget *button_widget;
107 GtkWidget *local_video;
108 GHashTable *remote_videos;
110 guint timeout_id;
111 PurpleMediaSessionType request_type;
114 static GType pidgin_media_get_type(void);
116 G_DEFINE_TYPE_WITH_PRIVATE(PidginMedia, pidgin_media,
117 GTK_TYPE_APPLICATION_WINDOW);
119 static void pidgin_media_dispose (GObject *object);
120 static void pidgin_media_finalize (GObject *object);
121 static void pidgin_media_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec);
122 static void pidgin_media_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec);
123 static void pidgin_media_set_state(PidginMedia *gtkmedia, PidginMediaState state);
125 #if 0
126 enum {
127 LAST_SIGNAL
129 static guint pidgin_media_signals[LAST_SIGNAL] = {0};
130 #endif
132 enum {
133 PROP_0,
134 PROP_MEDIA,
135 PROP_SCREENNAME
139 static void
140 pidgin_media_class_init (PidginMediaClass *klass)
142 GObjectClass *gobject_class = G_OBJECT_CLASS(klass);
143 /* GtkContainerClass *container_class = (GtkContainerClass*)klass; */
145 gobject_class->dispose = pidgin_media_dispose;
146 gobject_class->finalize = pidgin_media_finalize;
147 gobject_class->set_property = pidgin_media_set_property;
148 gobject_class->get_property = pidgin_media_get_property;
150 g_object_class_install_property(gobject_class, PROP_MEDIA,
151 g_param_spec_object("media",
152 "PurpleMedia",
153 "The PurpleMedia associated with this media.",
154 PURPLE_TYPE_MEDIA,
155 G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
156 g_object_class_install_property(gobject_class, PROP_SCREENNAME,
157 g_param_spec_string("screenname",
158 "Screenname",
159 "The screenname of the user this session is with.",
160 NULL,
161 G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
164 static void
165 pidgin_media_hangup_activate_cb(GSimpleAction *action, GVariant *parameter,
166 gpointer user_data)
168 PidginMedia *media = PIDGIN_MEDIA(user_data);
170 purple_media_stream_info(media->priv->media,
171 PURPLE_MEDIA_INFO_HANGUP, NULL, NULL, TRUE);
174 static void
175 pidgin_media_hold_change_state_cb(GSimpleAction *action, GVariant *value,
176 gpointer user_data)
178 PidginMedia *media = PIDGIN_MEDIA(user_data);
180 purple_media_stream_info(media->priv->media,
181 g_variant_get_boolean(value) ?
182 PURPLE_MEDIA_INFO_HOLD : PURPLE_MEDIA_INFO_UNHOLD,
183 NULL, NULL, TRUE);
185 g_simple_action_set_state(action, value);
188 static void
189 pidgin_media_mute_change_state_cb(GSimpleAction *action, GVariant *value,
190 gpointer user_data)
192 PidginMedia *media = PIDGIN_MEDIA(user_data);
194 purple_media_stream_info(media->priv->media,
195 g_variant_get_boolean(value) ?
196 PURPLE_MEDIA_INFO_MUTE : PURPLE_MEDIA_INFO_UNMUTE,
197 NULL, NULL, TRUE);
199 g_simple_action_set_state(action, value);
202 static void
203 pidgin_media_pause_change_state_cb(GSimpleAction *action, GVariant *value,
204 gpointer user_data)
206 PidginMedia *media = PIDGIN_MEDIA(user_data);
208 purple_media_stream_info(media->priv->media,
209 g_variant_get_boolean(value) ?
210 PURPLE_MEDIA_INFO_PAUSE : PURPLE_MEDIA_INFO_UNPAUSE,
211 NULL, NULL, TRUE);
213 g_simple_action_set_state(action, value);
216 static gboolean
217 pidgin_media_delete_event_cb(GtkWidget *widget,
218 GdkEvent *event, PidginMedia *media)
220 if (media->priv->media)
221 g_action_group_activate_action(G_ACTION_GROUP(media),
222 "Hangup", NULL);
223 return FALSE;
226 #ifdef HAVE_X11
227 static int
228 pidgin_x_error_handler(Display *display, XErrorEvent *event)
230 const gchar *error_type;
231 switch (event->error_code) {
232 #define XERRORCASE(type) case type: error_type = #type; break
233 XERRORCASE(BadAccess);
234 XERRORCASE(BadAlloc);
235 XERRORCASE(BadAtom);
236 XERRORCASE(BadColor);
237 XERRORCASE(BadCursor);
238 XERRORCASE(BadDrawable);
239 XERRORCASE(BadFont);
240 XERRORCASE(BadGC);
241 XERRORCASE(BadIDChoice);
242 XERRORCASE(BadImplementation);
243 XERRORCASE(BadLength);
244 XERRORCASE(BadMatch);
245 XERRORCASE(BadName);
246 XERRORCASE(BadPixmap);
247 XERRORCASE(BadRequest);
248 XERRORCASE(BadValue);
249 XERRORCASE(BadWindow);
250 #undef XERRORCASE
251 default:
252 error_type = "unknown";
253 break;
255 purple_debug_error("media", "A %s Xlib error has occurred. "
256 "The program would normally crash now.\n",
257 error_type);
258 return 0;
260 #endif
262 static const GActionEntry media_action_entries[] = {
263 { "Hangup", pidgin_media_hangup_activate_cb },
264 { "Hold", NULL, NULL, "false", pidgin_media_hold_change_state_cb },
265 { "Mute", NULL, NULL, "false", pidgin_media_mute_change_state_cb },
266 { "Pause", NULL, NULL, "false", pidgin_media_pause_change_state_cb },
269 static const gchar *media_menu =
270 "<interface>"
271 "<menu id='MediaMenu'>"
272 "<submenu>"
273 "<attribute name='label' translatable='yes'>_Media</attribute>"
274 "<section>"
275 "<item>"
276 "<attribute name='label' translatable='yes'>_Hangup</attribute>"
277 "<attribute name='action'>win.Hangup</attribute>"
278 "</item>"
279 "</section>"
280 "</submenu>"
281 "</menu>"
282 "</interface>";
284 static GtkWidget *
285 setup_menubar(PidginMedia *window)
287 GError *error;
288 GtkWidget *menu;
290 window->priv->ui = gtk_builder_new();
291 gtk_builder_set_translation_domain(window->priv->ui, PACKAGE);
293 error = NULL;
294 if (!gtk_builder_add_from_string(window->priv->ui, media_menu, -1,
295 &error))
297 g_message("building menus failed: %s", error->message);
298 g_error_free(error);
299 exit(EXIT_FAILURE);
302 menu = gtk_menu_bar_new_from_model(G_MENU_MODEL(
303 gtk_builder_get_object(window->priv->ui,
304 "MediaMenu")));
306 gtk_widget_show(menu);
307 return menu;
310 static void
311 pidgin_media_init (PidginMedia *media)
313 GtkWidget *vbox;
314 media->priv = pidgin_media_get_instance_private(media);
316 #ifdef HAVE_X11
317 XSetErrorHandler(pidgin_x_error_handler);
318 #endif
320 g_action_map_add_action_entries(G_ACTION_MAP(media),
321 media_action_entries,
322 G_N_ELEMENTS(media_action_entries), media);
324 vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
325 gtk_container_add(GTK_CONTAINER(media), vbox);
327 media->priv->statusbar = gtk_statusbar_new();
328 gtk_box_pack_end(GTK_BOX(vbox), media->priv->statusbar,
329 FALSE, FALSE, 0);
330 gtk_statusbar_push(GTK_STATUSBAR(media->priv->statusbar),
331 0, _("Calling..."));
332 gtk_widget_show(media->priv->statusbar);
334 media->priv->menubar = setup_menubar(media);
335 gtk_box_pack_start(GTK_BOX(vbox), media->priv->menubar,
336 FALSE, TRUE, 0);
338 media->priv->display = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, PIDGIN_HIG_BOX_SPACE);
339 gtk_container_set_border_width(GTK_CONTAINER(media->priv->display),
340 PIDGIN_HIG_BOX_SPACE);
341 gtk_box_pack_start(GTK_BOX(vbox), media->priv->display,
342 TRUE, TRUE, PIDGIN_HIG_BOX_SPACE);
343 gtk_widget_show(vbox);
345 g_signal_connect(G_OBJECT(media), "delete-event",
346 G_CALLBACK(pidgin_media_delete_event_cb), media);
348 media->priv->recv_progressbars =
349 g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL);
350 media->priv->remote_videos =
351 g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL);
354 static gchar *
355 create_key(const gchar *session_id, const gchar *participant)
357 return g_strdup_printf("%s_%s", session_id, participant);
360 static void
361 pidgin_media_insert_widget(PidginMedia *gtkmedia, GtkWidget *widget,
362 const gchar *session_id, const gchar *participant)
364 gchar *key = create_key(session_id, participant);
365 PurpleMediaSessionType type =
366 purple_media_get_session_type(gtkmedia->priv->media, session_id);
368 if (type & PURPLE_MEDIA_AUDIO)
369 g_hash_table_insert(gtkmedia->priv->recv_progressbars, key, widget);
370 else if (type & PURPLE_MEDIA_VIDEO)
371 g_hash_table_insert(gtkmedia->priv->remote_videos, key, widget);
374 static GtkWidget *
375 pidgin_media_get_widget(PidginMedia *gtkmedia,
376 const gchar *session_id, const gchar *participant)
378 GtkWidget *widget = NULL;
379 gchar *key = create_key(session_id, participant);
380 PurpleMediaSessionType type =
381 purple_media_get_session_type(gtkmedia->priv->media, session_id);
383 if (type & PURPLE_MEDIA_AUDIO)
384 widget = g_hash_table_lookup(gtkmedia->priv->recv_progressbars, key);
385 else if (type & PURPLE_MEDIA_VIDEO)
386 widget = g_hash_table_lookup(gtkmedia->priv->remote_videos, key);
388 g_free(key);
389 return widget;
392 static void
393 pidgin_media_remove_widget(PidginMedia *gtkmedia,
394 const gchar *session_id, const gchar *participant)
396 GtkWidget *widget = pidgin_media_get_widget(gtkmedia, session_id, participant);
398 if (widget) {
399 PurpleMediaSessionType type =
400 purple_media_get_session_type(gtkmedia->priv->media, session_id);
401 gchar *key = create_key(session_id, participant);
402 GtkRequisition req;
404 if (type & PURPLE_MEDIA_AUDIO) {
405 g_hash_table_remove(gtkmedia->priv->recv_progressbars, key);
407 if (g_hash_table_size(gtkmedia->priv->recv_progressbars) == 0 &&
408 gtkmedia->priv->send_progress) {
410 gtk_widget_destroy(gtkmedia->priv->send_progress);
411 gtkmedia->priv->send_progress = NULL;
413 gtk_widget_destroy(gtkmedia->priv->mute);
414 gtkmedia->priv->mute = NULL;
416 } else if (type & PURPLE_MEDIA_VIDEO) {
417 g_hash_table_remove(gtkmedia->priv->remote_videos, key);
419 if (g_hash_table_size(gtkmedia->priv->remote_videos) == 0 &&
420 gtkmedia->priv->local_video) {
422 gtk_widget_destroy(gtkmedia->priv->local_video);
423 gtkmedia->priv->local_video = NULL;
425 gtk_widget_destroy(gtkmedia->priv->pause);
426 gtkmedia->priv->pause = NULL;
430 g_free(key);
432 gtk_widget_destroy(widget);
434 gtk_widget_get_preferred_size(GTK_WIDGET(gtkmedia), NULL, &req);
435 gtk_window_resize(GTK_WINDOW(gtkmedia), req.width, req.height);
439 static void
440 level_message_cb(PurpleMedia *media, gchar *session_id, gchar *participant,
441 double level, PidginMedia *gtkmedia)
443 GtkWidget *progress = NULL;
444 if (participant == NULL)
445 progress = gtkmedia->priv->send_progress;
446 else
447 progress = pidgin_media_get_widget(gtkmedia, session_id, participant);
449 if (progress)
450 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(progress), level);
454 static void
455 pidgin_media_disconnect_levels(PurpleMedia *media, PidginMedia *gtkmedia)
457 PurpleMediaManager *manager = purple_media_get_manager(media);
458 GstElement *element = purple_media_manager_get_pipeline(manager);
459 gulong handler_id = g_signal_handler_find(G_OBJECT(gst_pipeline_get_bus(GST_PIPELINE(element))),
460 G_SIGNAL_MATCH_FUNC | G_SIGNAL_MATCH_DATA, 0, 0,
461 NULL, G_CALLBACK(level_message_cb), gtkmedia);
462 if (handler_id)
463 g_signal_handler_disconnect(G_OBJECT(gst_pipeline_get_bus(GST_PIPELINE(element))),
464 handler_id);
467 static void
468 pidgin_media_dispose(GObject *media)
470 PidginMedia *gtkmedia = PIDGIN_MEDIA(media);
471 purple_debug_info("gtkmedia", "pidgin_media_dispose\n");
473 if (gtkmedia->priv->media) {
474 purple_request_close_with_handle(gtkmedia);
475 purple_media_remove_output_windows(gtkmedia->priv->media);
476 pidgin_media_disconnect_levels(gtkmedia->priv->media, gtkmedia);
477 g_object_unref(gtkmedia->priv->media);
478 gtkmedia->priv->media = NULL;
481 if (gtkmedia->priv->ui) {
482 g_object_unref(gtkmedia->priv->ui);
483 gtkmedia->priv->ui = NULL;
486 if (gtkmedia->priv->timeout_id != 0)
487 g_source_remove(gtkmedia->priv->timeout_id);
489 if (gtkmedia->priv->recv_progressbars) {
490 g_hash_table_destroy(gtkmedia->priv->recv_progressbars);
491 g_hash_table_destroy(gtkmedia->priv->remote_videos);
492 gtkmedia->priv->recv_progressbars = NULL;
493 gtkmedia->priv->remote_videos = NULL;
496 G_OBJECT_CLASS(pidgin_media_parent_class)->dispose(media);
499 static void
500 pidgin_media_finalize(GObject *media)
502 /* PidginMedia *gtkmedia = PIDGIN_MEDIA(media); */
503 purple_debug_info("gtkmedia", "pidgin_media_finalize\n");
505 G_OBJECT_CLASS(pidgin_media_parent_class)->finalize(media);
508 static void
509 pidgin_media_emit_message(PidginMedia *gtkmedia, const char *msg)
511 PurpleConversation *conv = purple_conversations_find_with_account(
512 gtkmedia->priv->screenname,
513 purple_media_get_account(gtkmedia->priv->media));
514 if (conv != NULL)
515 purple_conversation_write_system_message(conv, msg, 0);
518 typedef struct
520 PidginMedia *gtkmedia;
521 gchar *session_id;
522 gchar *participant;
523 } PidginMediaRealizeData;
525 static gboolean
526 realize_cb_cb(PidginMediaRealizeData *data)
528 PidginMediaPrivate *priv = data->gtkmedia->priv;
529 GdkWindow *window = NULL;
531 if (data->participant == NULL)
532 window = gtk_widget_get_window(priv->local_video);
533 else {
534 GtkWidget *widget = pidgin_media_get_widget(data->gtkmedia,
535 data->session_id, data->participant);
536 if (widget)
537 window = gtk_widget_get_window(widget);
540 if (window) {
541 gulong window_id = 0;
542 #ifdef GDK_WINDOWING_WIN32
543 if (GDK_IS_WIN32_WINDOW(window))
544 window_id = GPOINTER_TO_UINT(GDK_WINDOW_HWND(window));
545 else
546 #endif
547 #ifdef GDK_WINDOWING_X11
548 if (GDK_IS_X11_WINDOW(window))
549 window_id = gdk_x11_window_get_xid(window);
550 else
551 #endif
552 #ifdef GDK_WINDOWING_QUARTZ
553 if (GDK_IS_QUARTZ_WINDOW(window))
554 window_id = (gulong)gdk_quartz_window_get_nsview(window);
555 else
556 #endif
557 g_warning("Unsupported GDK backend");
558 #if !(defined(GDK_WINDOWING_WIN32) \
559 || defined(GDK_WINDOWING_X11) \
560 || defined(GDK_WINDOWING_QUARTZ))
561 # error "Unsupported GDK windowing system"
562 #endif
564 purple_media_set_output_window(priv->media, data->session_id,
565 data->participant, window_id);
568 g_free(data->session_id);
569 g_free(data->participant);
570 g_free(data);
571 return FALSE;
574 static void
575 realize_cb(GtkWidget *widget, PidginMediaRealizeData *data)
577 g_timeout_add(0, (GSourceFunc)realize_cb_cb, data);
580 static void
581 pidgin_media_error_cb(PidginMedia *media, const char *error, PidginMedia *gtkmedia)
583 PurpleConversation *conv = purple_conversations_find_with_account(
584 gtkmedia->priv->screenname,
585 purple_media_get_account(gtkmedia->priv->media));
586 if (conv != NULL) {
587 purple_conversation_write_system_message(
588 conv, error, PURPLE_MESSAGE_ERROR);
589 } else {
590 purple_notify_error(NULL, NULL, _("Media error"), error,
591 purple_request_cpar_from_conversation(conv));
594 gtk_statusbar_push(GTK_STATUSBAR(gtkmedia->priv->statusbar),
595 0, error);
598 static void
599 pidgin_media_accept_cb(PurpleMedia *media, int index)
601 purple_media_stream_info(media, PURPLE_MEDIA_INFO_ACCEPT,
602 NULL, NULL, TRUE);
605 static void
606 pidgin_media_reject_cb(PurpleMedia *media, int index)
608 GList *iter = purple_media_get_session_ids(media);
609 for (; iter; iter = g_list_delete_link(iter, iter)) {
610 const gchar *sessionid = iter->data;
611 if (!purple_media_accepted(media, sessionid, NULL))
612 purple_media_stream_info(media, PURPLE_MEDIA_INFO_REJECT,
613 sessionid, NULL, TRUE);
617 static gboolean
618 pidgin_request_timeout_cb(PidginMedia *gtkmedia)
620 PurpleAccount *account;
621 PurpleBuddy *buddy;
622 const gchar *alias;
623 PurpleMediaSessionType type;
624 gchar *message = NULL;
626 account = purple_media_get_account(gtkmedia->priv->media);
627 buddy = purple_blist_find_buddy(account, gtkmedia->priv->screenname);
628 alias = buddy ? purple_buddy_get_contact_alias(buddy) :
629 gtkmedia->priv->screenname;
630 type = gtkmedia->priv->request_type;
631 gtkmedia->priv->timeout_id = 0;
633 if (type & PURPLE_MEDIA_AUDIO && type & PURPLE_MEDIA_VIDEO) {
634 message = g_strdup_printf(_("%s wishes to start an audio/video session with you."),
635 alias);
636 } else if (type & PURPLE_MEDIA_AUDIO) {
637 message = g_strdup_printf(_("%s wishes to start an audio session with you."),
638 alias);
639 } else if (type & PURPLE_MEDIA_VIDEO) {
640 message = g_strdup_printf(_("%s wishes to start a video session with you."),
641 alias);
644 gtkmedia->priv->request_type = PURPLE_MEDIA_NONE;
645 if (!purple_media_accepted(gtkmedia->priv->media, NULL, NULL)) {
646 purple_request_accept_cancel(gtkmedia, _("Incoming Call"),
647 message, NULL, PURPLE_DEFAULT_ACTION_NONE,
648 purple_request_cpar_from_account(account),
649 gtkmedia->priv->media, pidgin_media_accept_cb,
650 pidgin_media_reject_cb);
652 pidgin_media_emit_message(gtkmedia, message);
653 g_free(message);
654 return FALSE;
657 static void
658 pidgin_media_input_volume_changed(GtkScaleButton *range, double value,
659 PurpleMedia *media)
661 double val = (double)value * 100.0;
662 purple_media_set_input_volume(media, NULL, val);
665 static void
666 pidgin_media_output_volume_changed(GtkScaleButton *range, double value,
667 PurpleMedia *media)
669 double val = (double)value * 100.0;
670 purple_media_set_output_volume(media, NULL, NULL, val);
673 static void
674 destroy_parent_widget_cb(GtkWidget *widget, GtkWidget *parent)
676 g_return_if_fail(GTK_IS_WIDGET(parent));
678 gtk_widget_destroy(parent);
681 static GtkWidget *
682 pidgin_media_add_audio_widget(PidginMedia *gtkmedia,
683 PurpleMediaSessionType type, const gchar *sid)
685 GtkWidget *volume_widget, *progress_parent, *volume, *progress;
686 double value;
688 static const gchar * input_volume_icons[] = {
689 "microphone-sensitivity-muted-symbolic",
690 "microphone-sensitivity-high-symbolic",
691 "microphone-sensitivity-low-symbolic",
692 "microphone-sensitivity-medium-symbolic",
693 NULL
696 if (type & PURPLE_MEDIA_SEND_AUDIO) {
697 value = purple_prefs_get_int(
698 "/purple/media/audio/volume/input");
699 } else if (type & PURPLE_MEDIA_RECV_AUDIO) {
700 value = purple_prefs_get_int(
701 "/purple/media/audio/volume/output");
702 } else
703 g_return_val_if_reached(NULL);
705 /* Setup widget structure */
706 volume_widget = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, PIDGIN_HIG_BOX_SPACE);
707 progress_parent = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
708 gtk_box_pack_start(GTK_BOX(volume_widget),
709 progress_parent, TRUE, TRUE, 0);
711 /* Volume button */
712 volume = gtk_volume_button_new();
713 gtk_scale_button_set_value(GTK_SCALE_BUTTON(volume), value/100.0);
714 gtk_box_pack_end(GTK_BOX(volume_widget),
715 volume, FALSE, FALSE, 0);
717 /* Volume level indicator */
718 progress = gtk_progress_bar_new();
719 gtk_widget_set_size_request(progress, 250, 10);
720 gtk_box_pack_end(GTK_BOX(progress_parent), progress, TRUE, FALSE, 0);
722 if (type & PURPLE_MEDIA_SEND_AUDIO) {
723 g_signal_connect (G_OBJECT(volume), "value-changed",
724 G_CALLBACK(pidgin_media_input_volume_changed),
725 gtkmedia->priv->media);
726 gtk_scale_button_set_icons(GTK_SCALE_BUTTON(volume),
727 input_volume_icons);
729 gtkmedia->priv->send_progress = progress;
730 } else if (type & PURPLE_MEDIA_RECV_AUDIO) {
731 g_signal_connect (G_OBJECT(volume), "value-changed",
732 G_CALLBACK(pidgin_media_output_volume_changed),
733 gtkmedia->priv->media);
735 pidgin_media_insert_widget(gtkmedia, progress, sid, gtkmedia->priv->screenname);
738 g_signal_connect(G_OBJECT(progress), "destroy",
739 G_CALLBACK(destroy_parent_widget_cb),
740 volume_widget);
742 gtk_widget_show_all(volume_widget);
744 return volume_widget;
747 static void
748 phone_dtmf_pressed_cb(GtkButton *button, gpointer user_data)
750 PidginMedia *gtkmedia = user_data;
751 gint num;
752 gchar *sid;
754 num = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(button), "dtmf-digit"));
755 sid = g_object_get_data(G_OBJECT(button), "session-id");
757 purple_media_send_dtmf(gtkmedia->priv->media, sid, num, 25, 50);
760 static inline GtkWidget *
761 phone_create_button(const gchar *text_hi, const gchar *text_lo)
763 GtkWidget *button;
764 GtkWidget *label_hi;
765 GtkWidget *label_lo;
766 GtkWidget *grid;
767 const gchar *text_hi_local;
769 if (text_hi)
770 text_hi_local = _(text_hi);
771 else
772 text_hi_local = "";
774 grid = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
775 gtk_box_set_homogeneous(GTK_BOX(grid), TRUE);
777 button = gtk_button_new();
778 label_hi = gtk_label_new(text_hi_local);
779 gtk_box_pack_end(GTK_BOX(grid), label_hi, FALSE, TRUE, 0);
780 label_lo = gtk_label_new(text_lo);
781 gtk_label_set_use_markup(GTK_LABEL(label_lo), TRUE);
782 gtk_box_pack_end(GTK_BOX(grid), label_lo, FALSE, TRUE, 0);
783 gtk_container_add(GTK_CONTAINER(button), grid);
785 return button;
788 static struct phone_label {
789 gchar *subtext;
790 gchar *text;
791 gchar chr;
792 } phone_labels[] = {
793 {"<b>1</b>", NULL, '1'},
794 /* Translators note: These are the letters on the keys of a numeric
795 keypad; translate according to the tables in §7 of ETSI ES 202 130:
796 http://webapp.etsi.org/WorkProgram/Report_WorkItem.asp?WKI_ID=11730
798 /* Letters on the '2' key of a numeric keypad */
799 {"<b>2</b>", N_("ABC"), '2'},
800 /* Letters on the '3' key of a numeric keypad */
801 {"<b>3</b>", N_("DEF"), '3'},
802 /* Letters on the '4' key of a numeric keypad */
803 {"<b>4</b>", N_("GHI"), '4'},
804 /* Letters on the '5' key of a numeric keypad */
805 {"<b>5</b>", N_("JKL"), '5'},
806 /* Letters on the '6' key of a numeric keypad */
807 {"<b>6</b>", N_("MNO"), '6'},
808 /* Letters on the '7' key of a numeric keypad */
809 {"<b>7</b>", N_("PQRS"), '7'},
810 /* Letters on the '8' key of a numeric keypad */
811 {"<b>8</b>", N_("TUV"), '8'},
812 /* Letters on the '9' key of a numeric keypad */
813 {"<b>9</b>", N_("WXYZ"), '9'},
814 {"<b>*</b>", NULL, '*'},
815 {"<b>0</b>", NULL, '0'},
816 {"<b>#</b>", NULL, '#'},
817 {NULL, NULL, 0}
820 static gboolean
821 pidgin_media_dtmf_key_press_event_cb(GtkWidget *widget,
822 GdkEvent *event, gpointer user_data)
824 PidginMedia *gtkmedia = user_data;
825 GdkEventKey *key = (GdkEventKey *) event;
827 if (event->type != GDK_KEY_PRESS) {
828 return FALSE;
831 if ((key->keyval >= GDK_KEY_0 && key->keyval <= GDK_KEY_9) ||
832 key->keyval == GDK_KEY_asterisk ||
833 key->keyval == GDK_KEY_numbersign) {
834 gchar *sid = g_object_get_data(G_OBJECT(widget), "session-id");
836 purple_media_send_dtmf(gtkmedia->priv->media, sid, key->keyval, 25, 50);
839 return FALSE;
842 static GtkWidget *
843 pidgin_media_add_dtmf_widget(PidginMedia *gtkmedia,
844 PurpleMediaSessionType type, const gchar *_sid)
846 GtkWidget *grid = gtk_grid_new();
847 GtkWidget *button;
848 gint index = 0;
849 GtkApplicationWindow *win = &gtkmedia->parent;
851 gtk_grid_set_row_homogeneous(GTK_GRID(grid), TRUE);
852 gtk_grid_set_column_homogeneous(GTK_GRID(grid), TRUE);
854 /* Add buttons */
855 for (index = 0; phone_labels[index].subtext != NULL; index++) {
856 button = phone_create_button(phone_labels[index].text,
857 phone_labels[index].subtext);
858 g_signal_connect(button, "pressed",
859 G_CALLBACK(phone_dtmf_pressed_cb), gtkmedia);
860 g_object_set_data(G_OBJECT(button), "dtmf-digit",
861 GINT_TO_POINTER(phone_labels[index].chr));
862 g_object_set_data_full(G_OBJECT(button), "session-id",
863 g_strdup(_sid), g_free);
864 gtk_grid_attach(GTK_GRID(grid), button,
865 index % 3, index / 3, 1, 1);
866 g_object_set(button, "expand", TRUE, "margin", 2, NULL);
869 g_signal_connect(G_OBJECT(win), "key-press-event",
870 G_CALLBACK(pidgin_media_dtmf_key_press_event_cb), gtkmedia);
871 g_object_set_data_full(G_OBJECT(win), "session-id",
872 g_strdup(_sid), g_free);
874 gtk_widget_show_all(grid);
876 return grid;
879 static void
880 pidgin_media_ready_cb(PurpleMedia *media, PidginMedia *gtkmedia, const gchar *sid)
882 GtkWidget *send_widget = NULL, *recv_widget = NULL, *button_widget = NULL;
883 PurpleMediaSessionType type =
884 purple_media_get_session_type(media, sid);
886 if (gtkmedia->priv->recv_widget == NULL
887 && type & (PURPLE_MEDIA_RECV_VIDEO |
888 PURPLE_MEDIA_RECV_AUDIO)) {
889 recv_widget = gtk_box_new(GTK_ORIENTATION_VERTICAL, PIDGIN_HIG_BOX_SPACE);
890 gtk_box_pack_start(GTK_BOX(gtkmedia->priv->display),
891 recv_widget, TRUE, TRUE, 0);
892 gtk_widget_show(recv_widget);
893 } else {
894 recv_widget = gtkmedia->priv->recv_widget;
896 if (gtkmedia->priv->send_widget == NULL
897 && type & (PURPLE_MEDIA_SEND_VIDEO |
898 PURPLE_MEDIA_SEND_AUDIO)) {
899 send_widget = gtk_box_new(GTK_ORIENTATION_VERTICAL, PIDGIN_HIG_BOX_SPACE);
900 gtk_box_pack_start(GTK_BOX(gtkmedia->priv->display),
901 send_widget, FALSE, TRUE, 0);
902 button_widget = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, PIDGIN_HIG_BOX_SPACE);
903 gtk_box_pack_end(GTK_BOX(recv_widget), button_widget,
904 FALSE, TRUE, 0);
905 gtk_widget_show(send_widget);
907 /* Hold button */
908 gtkmedia->priv->hold =
909 gtk_toggle_button_new_with_mnemonic(_("_Hold"));
910 gtk_box_pack_end(GTK_BOX(button_widget), gtkmedia->priv->hold,
911 FALSE, FALSE, 0);
912 gtk_widget_show(gtkmedia->priv->hold);
913 gtk_actionable_set_action_name(
914 GTK_ACTIONABLE(gtkmedia->priv->hold),
915 "win.Hold");
916 } else {
917 send_widget = gtkmedia->priv->send_widget;
918 button_widget = gtkmedia->priv->button_widget;
921 if (type & PURPLE_MEDIA_RECV_VIDEO) {
922 PidginMediaRealizeData *data;
923 GtkWidget *aspect;
924 GtkWidget *remote_video;
926 aspect = gtk_aspect_frame_new(NULL, 0, 0, 4.0/3.0, FALSE);
927 gtk_frame_set_shadow_type(GTK_FRAME(aspect), GTK_SHADOW_IN);
928 gtk_box_pack_start(GTK_BOX(recv_widget), aspect, TRUE, TRUE, 0);
930 data = g_new0(PidginMediaRealizeData, 1);
931 data->gtkmedia = gtkmedia;
932 data->session_id = g_strdup(sid);
933 data->participant = g_strdup(gtkmedia->priv->screenname);
935 remote_video = pidgin_create_video_widget();
936 g_signal_connect(G_OBJECT(remote_video), "realize",
937 G_CALLBACK(realize_cb), data);
938 gtk_container_add(GTK_CONTAINER(aspect), remote_video);
939 gtk_widget_set_size_request (GTK_WIDGET(remote_video), 320, 240);
940 g_signal_connect(G_OBJECT(remote_video), "destroy",
941 G_CALLBACK(destroy_parent_widget_cb), aspect);
943 gtk_widget_show(remote_video);
944 gtk_widget_show(aspect);
946 pidgin_media_insert_widget(gtkmedia, remote_video,
947 data->session_id, data->participant);
950 if (type & PURPLE_MEDIA_SEND_VIDEO && !gtkmedia->priv->local_video) {
951 PidginMediaRealizeData *data;
952 GtkWidget *aspect;
953 GtkWidget *local_video;
955 aspect = gtk_aspect_frame_new(NULL, 0, 0, 4.0/3.0, TRUE);
956 gtk_frame_set_shadow_type(GTK_FRAME(aspect), GTK_SHADOW_IN);
957 gtk_box_pack_start(GTK_BOX(send_widget), aspect, FALSE, TRUE, 0);
959 data = g_new0(PidginMediaRealizeData, 1);
960 data->gtkmedia = gtkmedia;
961 data->session_id = g_strdup(sid);
962 data->participant = NULL;
964 local_video = pidgin_create_video_widget();
965 g_signal_connect(G_OBJECT(local_video), "realize",
966 G_CALLBACK(realize_cb), data);
967 gtk_container_add(GTK_CONTAINER(aspect), local_video);
968 gtk_widget_set_size_request (GTK_WIDGET(local_video), 80, 60);
969 g_signal_connect(G_OBJECT(local_video), "destroy",
970 G_CALLBACK(destroy_parent_widget_cb), aspect);
972 gtk_widget_show(local_video);
973 gtk_widget_show(aspect);
975 gtkmedia->priv->pause =
976 gtk_toggle_button_new_with_mnemonic(_("_Pause"));
977 gtk_box_pack_end(GTK_BOX(button_widget), gtkmedia->priv->pause,
978 FALSE, FALSE, 0);
979 gtk_widget_show(gtkmedia->priv->pause);
980 gtk_actionable_set_action_name(
981 GTK_ACTIONABLE(gtkmedia->priv->pause),
982 "win.Pause");
984 gtkmedia->priv->local_video = local_video;
986 if (type & PURPLE_MEDIA_RECV_AUDIO) {
987 gtk_box_pack_end(GTK_BOX(recv_widget),
988 pidgin_media_add_audio_widget(gtkmedia,
989 PURPLE_MEDIA_RECV_AUDIO, sid), FALSE, FALSE, 0);
992 if (type & PURPLE_MEDIA_SEND_AUDIO) {
993 gtkmedia->priv->mute =
994 gtk_toggle_button_new_with_mnemonic(_("_Mute"));
995 gtk_box_pack_end(GTK_BOX(button_widget), gtkmedia->priv->mute,
996 FALSE, FALSE, 0);
997 gtk_widget_show(gtkmedia->priv->mute);
998 gtk_actionable_set_action_name(
999 GTK_ACTIONABLE(gtkmedia->priv->mute),
1000 "win.Mute");
1002 gtk_box_pack_end(GTK_BOX(recv_widget),
1003 pidgin_media_add_audio_widget(gtkmedia,
1004 PURPLE_MEDIA_SEND_AUDIO, sid), FALSE, FALSE, 0);
1006 gtk_box_pack_end(GTK_BOX(recv_widget),
1007 pidgin_media_add_dtmf_widget(gtkmedia,
1008 PURPLE_MEDIA_SEND_AUDIO, sid), FALSE, FALSE, 0);
1011 if (type & PURPLE_MEDIA_AUDIO &&
1012 gtkmedia->priv->level_handler_id == 0) {
1013 gtkmedia->priv->level_handler_id = g_signal_connect(
1014 media, "level", G_CALLBACK(level_message_cb),
1015 gtkmedia);
1018 if (send_widget != NULL)
1019 gtkmedia->priv->send_widget = send_widget;
1020 if (recv_widget != NULL)
1021 gtkmedia->priv->recv_widget = recv_widget;
1022 if (button_widget != NULL) {
1023 gtkmedia->priv->button_widget = button_widget;
1024 gtk_widget_show(GTK_WIDGET(button_widget));
1027 if (purple_media_is_initiator(media, sid, NULL) == FALSE) {
1028 if (gtkmedia->priv->timeout_id != 0)
1029 g_source_remove(gtkmedia->priv->timeout_id);
1030 gtkmedia->priv->request_type |= type;
1031 gtkmedia->priv->timeout_id = g_timeout_add(500,
1032 (GSourceFunc)pidgin_request_timeout_cb,
1033 gtkmedia);
1036 /* set the window icon according to the type */
1037 if (type & PURPLE_MEDIA_VIDEO) {
1038 gtk_window_set_icon_name(GTK_WINDOW(gtkmedia), "video-call");
1039 } else if (type & PURPLE_MEDIA_AUDIO) {
1040 gtk_window_set_icon_name(GTK_WINDOW(gtkmedia), "audio-call");
1043 gtk_widget_show(gtkmedia->priv->display);
1046 static void
1047 pidgin_media_state_changed_cb(PurpleMedia *media, PurpleMediaState state,
1048 gchar *sid, gchar *name, PidginMedia *gtkmedia)
1050 purple_debug_info("gtkmedia", "state: %d sid: %s name: %s\n",
1051 state, sid ? sid : "(null)", name ? name : "(null)");
1052 if (state == PURPLE_MEDIA_STATE_END) {
1053 if (sid != NULL && name != NULL) {
1054 pidgin_media_remove_widget(gtkmedia, sid, name);
1055 } else if (sid == NULL && name == NULL) {
1056 pidgin_media_emit_message(gtkmedia,
1057 _("The call has been terminated."));
1058 gtk_widget_destroy(GTK_WIDGET(gtkmedia));
1060 } else if (state == PURPLE_MEDIA_STATE_NEW &&
1061 sid != NULL && name != NULL) {
1062 pidgin_media_ready_cb(media, gtkmedia, sid);
1066 static void
1067 pidgin_media_stream_info_cb(PurpleMedia *media, PurpleMediaInfoType type,
1068 gchar *sid, gchar *name, gboolean local,
1069 PidginMedia *gtkmedia)
1071 if (type == PURPLE_MEDIA_INFO_REJECT) {
1072 pidgin_media_emit_message(gtkmedia,
1073 _("You have rejected the call."));
1074 } else if (type == PURPLE_MEDIA_INFO_ACCEPT) {
1075 if (local == TRUE)
1076 purple_request_close_with_handle(gtkmedia);
1077 pidgin_media_set_state(gtkmedia, PIDGIN_MEDIA_ACCEPTED);
1078 pidgin_media_emit_message(gtkmedia, _("Call in progress."));
1079 gtk_statusbar_push(GTK_STATUSBAR(gtkmedia->priv->statusbar),
1080 0, _("Call in progress"));
1081 gtk_widget_show(GTK_WIDGET(gtkmedia));
1082 } else if (type == PURPLE_MEDIA_INFO_MUTE && !local) {
1083 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(gtkmedia->priv->mute), TRUE);
1084 } else if (type == PURPLE_MEDIA_INFO_UNMUTE && !local) {
1085 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(gtkmedia->priv->mute), FALSE);
1089 static void
1090 pidgin_media_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec)
1092 PidginMedia *media;
1093 g_return_if_fail(PIDGIN_IS_MEDIA(object));
1095 media = PIDGIN_MEDIA(object);
1096 switch (prop_id) {
1097 case PROP_MEDIA:
1099 if (media->priv->media)
1100 g_object_unref(media->priv->media);
1101 media->priv->media = g_value_dup_object(value);
1103 if (purple_media_is_initiator(media->priv->media,
1104 NULL, NULL) == TRUE)
1105 pidgin_media_set_state(media, PIDGIN_MEDIA_WAITING);
1106 else
1107 pidgin_media_set_state(media, PIDGIN_MEDIA_REQUESTED);
1109 g_signal_connect(G_OBJECT(media->priv->media), "error",
1110 G_CALLBACK(pidgin_media_error_cb), media);
1111 g_signal_connect(G_OBJECT(media->priv->media), "state-changed",
1112 G_CALLBACK(pidgin_media_state_changed_cb), media);
1113 g_signal_connect(G_OBJECT(media->priv->media), "stream-info",
1114 G_CALLBACK(pidgin_media_stream_info_cb), media);
1115 break;
1117 case PROP_SCREENNAME:
1118 g_free(media->priv->screenname);
1119 media->priv->screenname = g_value_dup_string(value);
1120 break;
1121 default:
1122 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1123 break;
1127 static void
1128 pidgin_media_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
1130 PidginMedia *media;
1131 g_return_if_fail(PIDGIN_IS_MEDIA(object));
1133 media = PIDGIN_MEDIA(object);
1135 switch (prop_id) {
1136 case PROP_MEDIA:
1137 g_value_set_object(value, media->priv->media);
1138 break;
1139 case PROP_SCREENNAME:
1140 g_value_set_string(value, media->priv->screenname);
1141 break;
1142 default:
1143 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1144 break;
1148 static GtkWidget *
1149 pidgin_media_new(PurpleMedia *media, const gchar *screenname)
1151 PidginMedia *gtkmedia = g_object_new(pidgin_media_get_type(),
1152 "media", media,
1153 "screenname", screenname, NULL);
1154 return GTK_WIDGET(gtkmedia);
1157 static void
1158 pidgin_media_set_state(PidginMedia *gtkmedia, PidginMediaState state)
1160 gtkmedia->priv->state = state;
1163 static gboolean
1164 pidgin_media_new_cb(PurpleMediaManager *manager, PurpleMedia *media,
1165 PurpleAccount *account, gchar *screenname, gpointer nul)
1167 PidginMedia *gtkmedia = PIDGIN_MEDIA(
1168 pidgin_media_new(media, screenname));
1169 PurpleBuddy *buddy = purple_blist_find_buddy(account, screenname);
1170 const gchar *alias = buddy ?
1171 purple_buddy_get_contact_alias(buddy) : screenname;
1172 gtk_window_set_title(GTK_WINDOW(gtkmedia), alias);
1174 if (purple_media_is_initiator(media, NULL, NULL) == TRUE)
1175 gtk_widget_show(GTK_WIDGET(gtkmedia));
1177 return TRUE;
1179 #endif /* USE_VV */
1181 void
1182 pidgin_medias_init(void)
1184 #ifdef USE_VV
1185 PurpleMediaManager *manager = purple_media_manager_get();
1186 PurpleMediaElementInfo *video_src = NULL;
1187 PurpleMediaElementInfo *video_sink = NULL;
1188 PurpleMediaElementInfo *audio_src = NULL;
1189 PurpleMediaElementInfo *audio_sink = NULL;
1190 const char *pref;
1192 pref = purple_prefs_get_string(
1193 PIDGIN_PREFS_ROOT "/vvconfig/video/src/device");
1194 if (pref)
1195 video_src = purple_media_manager_get_element_info(manager, pref);
1196 if (!video_src) {
1197 pref = "autovideosrc";
1198 purple_prefs_set_string(
1199 PIDGIN_PREFS_ROOT "/vvconfig/video/src/device", pref);
1200 video_src = purple_media_manager_get_element_info(manager,
1201 pref);
1204 pref = purple_prefs_get_string(
1205 PIDGIN_PREFS_ROOT "/vvconfig/video/sink/device");
1206 if (pref)
1207 video_sink = purple_media_manager_get_element_info(manager, pref);
1208 if (!video_sink) {
1209 pref = "autovideosink";
1210 purple_prefs_set_string(
1211 PIDGIN_PREFS_ROOT "/vvconfig/video/sink/device", pref);
1212 video_sink = purple_media_manager_get_element_info(manager,
1213 pref);
1216 pref = purple_prefs_get_string(
1217 PIDGIN_PREFS_ROOT "/vvconfig/audio/src/device");
1218 if (pref)
1219 audio_src = purple_media_manager_get_element_info(manager, pref);
1220 if (!audio_src) {
1221 pref = "autoaudiosrc";
1222 purple_prefs_set_string(
1223 PIDGIN_PREFS_ROOT "/vvconfig/audio/src/device", pref);
1224 audio_src = purple_media_manager_get_element_info(manager,
1225 pref);
1228 pref = purple_prefs_get_string(
1229 PIDGIN_PREFS_ROOT "/vvconfig/audio/sink/device");
1230 if (pref)
1231 audio_sink = purple_media_manager_get_element_info(manager, pref);
1232 if (!audio_sink) {
1233 pref = "autoaudiosink";
1234 purple_prefs_set_string(
1235 PIDGIN_PREFS_ROOT "/vvconfig/audio/sink/device", pref);
1236 audio_sink = purple_media_manager_get_element_info(manager,
1237 pref);
1240 g_signal_connect(G_OBJECT(manager), "init-media",
1241 G_CALLBACK(pidgin_media_new_cb), NULL);
1243 purple_media_manager_set_ui_caps(manager,
1244 PURPLE_MEDIA_CAPS_AUDIO |
1245 PURPLE_MEDIA_CAPS_AUDIO_SINGLE_DIRECTION |
1246 PURPLE_MEDIA_CAPS_VIDEO |
1247 PURPLE_MEDIA_CAPS_VIDEO_SINGLE_DIRECTION |
1248 PURPLE_MEDIA_CAPS_AUDIO_VIDEO);
1250 purple_debug_info("gtkmedia", "Registering media element types\n");
1251 purple_media_manager_set_active_element(manager, video_src);
1252 purple_media_manager_set_active_element(manager, video_sink);
1253 purple_media_manager_set_active_element(manager, audio_src);
1254 purple_media_manager_set_active_element(manager, audio_sink);
1255 #endif