2 * @file media.c Account API
7 * Pidgin is the legal property of its developers, whose names are too numerous
8 * to list here. Please refer to the COPYRIGHT file distributed with this
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
21 * You should have received a copy of the GNU General Public License
22 * along with this program; if not, write to the Free Software
23 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
28 #include "connection.h"
30 #include "mediamanager.h"
36 #include "pidginstock.h"
39 #include "media-gst.h"
42 #include <gdk/gdkwin32.h>
45 #include <gst/interfaces/xoverlay.h>
47 #define PIDGIN_TYPE_MEDIA (pidgin_media_get_type())
48 #define PIDGIN_MEDIA(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), PIDGIN_TYPE_MEDIA, PidginMedia))
49 #define PIDGIN_MEDIA_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), PIDGIN_TYPE_MEDIA, PidginMediaClass))
50 #define PIDGIN_IS_MEDIA(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), PIDGIN_TYPE_MEDIA))
51 #define PIDGIN_IS_MEDIA_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), PIDGIN_TYPE_MEDIA))
52 #define PIDGIN_MEDIA_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), PIDGIN_TYPE_MEDIA, PidginMediaClass))
54 typedef struct _PidginMedia PidginMedia
;
55 typedef struct _PidginMediaClass PidginMediaClass
;
56 typedef struct _PidginMediaPrivate PidginMediaPrivate
;
60 /* Waiting for response */
61 PIDGIN_MEDIA_WAITING
= 1,
63 PIDGIN_MEDIA_REQUESTED
,
65 PIDGIN_MEDIA_ACCEPTED
,
67 PIDGIN_MEDIA_REJECTED
,
70 struct _PidginMediaClass
72 GtkWindowClass parent_class
;
78 PidginMediaPrivate
*priv
;
81 struct _PidginMediaPrivate
85 gulong level_handler_id
;
95 GtkWidget
*send_progress
;
96 GHashTable
*recv_progressbars
;
98 PidginMediaState state
;
101 GtkWidget
*send_widget
;
102 GtkWidget
*recv_widget
;
103 GtkWidget
*button_widget
;
104 GtkWidget
*local_video
;
105 GHashTable
*remote_videos
;
108 PurpleMediaSessionType request_type
;
111 #define PIDGIN_MEDIA_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE((obj), PIDGIN_TYPE_MEDIA, PidginMediaPrivate))
113 static void pidgin_media_class_init (PidginMediaClass
*klass
);
114 static void pidgin_media_init (PidginMedia
*media
);
115 static void pidgin_media_dispose (GObject
*object
);
116 static void pidgin_media_finalize (GObject
*object
);
117 static void pidgin_media_get_property (GObject
*object
, guint prop_id
, GValue
*value
, GParamSpec
*pspec
);
118 static void pidgin_media_set_property (GObject
*object
, guint prop_id
, const GValue
*value
, GParamSpec
*pspec
);
119 static void pidgin_media_set_state(PidginMedia
*gtkmedia
, PidginMediaState state
);
121 static GtkWindowClass
*parent_class
= NULL
;
128 static guint pidgin_media_signals
[LAST_SIGNAL
] = {0};
138 pidgin_media_get_type(void)
140 static GType type
= 0;
143 static const GTypeInfo info
= {
144 sizeof(PidginMediaClass
),
147 (GClassInitFunc
) pidgin_media_class_init
,
152 (GInstanceInitFunc
) pidgin_media_init
,
155 type
= g_type_register_static(GTK_TYPE_WINDOW
, "PidginMedia", &info
, 0);
162 pidgin_media_class_init (PidginMediaClass
*klass
)
164 GObjectClass
*gobject_class
= (GObjectClass
*)klass
;
165 /* GtkContainerClass *container_class = (GtkContainerClass*)klass; */
166 parent_class
= g_type_class_peek_parent(klass
);
168 gobject_class
->dispose
= pidgin_media_dispose
;
169 gobject_class
->finalize
= pidgin_media_finalize
;
170 gobject_class
->set_property
= pidgin_media_set_property
;
171 gobject_class
->get_property
= pidgin_media_get_property
;
173 g_object_class_install_property(gobject_class
, PROP_MEDIA
,
174 g_param_spec_object("media",
176 "The PurpleMedia associated with this media.",
178 G_PARAM_CONSTRUCT_ONLY
| G_PARAM_READWRITE
));
179 g_object_class_install_property(gobject_class
, PROP_SCREENNAME
,
180 g_param_spec_string("screenname",
182 "The screenname of the user this session is with.",
184 G_PARAM_CONSTRUCT_ONLY
| G_PARAM_READWRITE
));
186 g_type_class_add_private(klass
, sizeof(PidginMediaPrivate
));
190 pidgin_media_hold_toggled(GtkToggleButton
*toggle
, PidginMedia
*media
)
192 purple_media_stream_info(media
->priv
->media
,
193 gtk_toggle_button_get_active(toggle
) ?
194 PURPLE_MEDIA_INFO_HOLD
: PURPLE_MEDIA_INFO_UNHOLD
,
199 pidgin_media_mute_toggled(GtkToggleButton
*toggle
, PidginMedia
*media
)
201 purple_media_stream_info(media
->priv
->media
,
202 gtk_toggle_button_get_active(toggle
) ?
203 PURPLE_MEDIA_INFO_MUTE
: PURPLE_MEDIA_INFO_UNMUTE
,
208 pidgin_media_pause_toggled(GtkToggleButton
*toggle
, PidginMedia
*media
)
210 purple_media_stream_info(media
->priv
->media
,
211 gtk_toggle_button_get_active(toggle
) ?
212 PURPLE_MEDIA_INFO_PAUSE
: PURPLE_MEDIA_INFO_UNPAUSE
,
217 pidgin_media_delete_event_cb(GtkWidget
*widget
,
218 GdkEvent
*event
, PidginMedia
*media
)
220 if (media
->priv
->media
)
221 purple_media_stream_info(media
->priv
->media
,
222 PURPLE_MEDIA_INFO_HANGUP
, NULL
, NULL
, TRUE
);
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
);
236 XERRORCASE(BadColor
);
237 XERRORCASE(BadCursor
);
238 XERRORCASE(BadDrawable
);
241 XERRORCASE(BadIDChoice
);
242 XERRORCASE(BadImplementation
);
243 XERRORCASE(BadLength
);
244 XERRORCASE(BadMatch
);
246 XERRORCASE(BadPixmap
);
247 XERRORCASE(BadRequest
);
248 XERRORCASE(BadValue
);
249 XERRORCASE(BadWindow
);
252 error_type
= "unknown";
255 purple_debug_error("media", "A %s Xlib error has occurred. "
256 "The program would normally crash now.\n",
263 menu_hangup(GtkAction
*action
, gpointer data
)
265 PidginMedia
*gtkmedia
= PIDGIN_MEDIA(data
);
266 purple_media_stream_info(gtkmedia
->priv
->media
,
267 PURPLE_MEDIA_INFO_HANGUP
, NULL
, NULL
, TRUE
);
270 static const GtkActionEntry menu_entries
[] = {
271 { "MediaMenu", NULL
, N_("_Media"), NULL
, NULL
, NULL
},
272 { "Hangup", NULL
, N_("_Hangup"), NULL
, NULL
, G_CALLBACK(menu_hangup
) },
275 static const char *media_menu
=
277 "<menubar name='Media'>"
278 "<menu action='MediaMenu'>"
279 "<menuitem action='Hangup'/>"
285 setup_menubar(PidginMedia
*window
)
287 GtkActionGroup
*action_group
;
289 GtkAccelGroup
*accel_group
;
292 action_group
= gtk_action_group_new("MediaActions");
294 gtk_action_group_set_translation_domain(action_group
,
297 gtk_action_group_add_actions(action_group
,
299 G_N_ELEMENTS(menu_entries
),
302 window
->priv
->ui
= gtk_ui_manager_new();
303 gtk_ui_manager_insert_action_group(window
->priv
->ui
, action_group
, 0);
305 accel_group
= gtk_ui_manager_get_accel_group(window
->priv
->ui
);
306 gtk_window_add_accel_group(GTK_WINDOW(window
), accel_group
);
309 if (!gtk_ui_manager_add_ui_from_string(window
->priv
->ui
, media_menu
, -1, &error
))
311 g_message("building menus failed: %s", error
->message
);
316 menu
= gtk_ui_manager_get_widget(window
->priv
->ui
, "/Media");
318 gtk_widget_show(menu
);
323 pidgin_media_init (PidginMedia
*media
)
326 media
->priv
= PIDGIN_MEDIA_GET_PRIVATE(media
);
329 XSetErrorHandler(pidgin_x_error_handler
);
332 vbox
= gtk_vbox_new(FALSE
, 0);
333 gtk_container_add(GTK_CONTAINER(media
), vbox
);
335 media
->priv
->statusbar
= gtk_statusbar_new();
336 gtk_box_pack_end(GTK_BOX(vbox
), media
->priv
->statusbar
,
338 gtk_statusbar_push(GTK_STATUSBAR(media
->priv
->statusbar
),
340 gtk_widget_show(media
->priv
->statusbar
);
342 media
->priv
->menubar
= setup_menubar(media
);
343 gtk_box_pack_start(GTK_BOX(vbox
), media
->priv
->menubar
,
346 media
->priv
->display
= gtk_hbox_new(FALSE
, PIDGIN_HIG_BOX_SPACE
);
347 gtk_container_set_border_width(GTK_CONTAINER(media
->priv
->display
),
348 PIDGIN_HIG_BOX_SPACE
);
349 gtk_box_pack_start(GTK_BOX(vbox
), media
->priv
->display
,
350 TRUE
, TRUE
, PIDGIN_HIG_BOX_SPACE
);
351 gtk_widget_show(vbox
);
353 g_signal_connect(G_OBJECT(media
), "delete-event",
354 G_CALLBACK(pidgin_media_delete_event_cb
), media
);
356 media
->priv
->recv_progressbars
=
357 g_hash_table_new_full(g_str_hash
, g_str_equal
, g_free
, NULL
);
358 media
->priv
->remote_videos
=
359 g_hash_table_new_full(g_str_hash
, g_str_equal
, g_free
, NULL
);
363 create_key(const gchar
*session_id
, const gchar
*participant
)
365 return g_strdup_printf("%s_%s", session_id
, participant
);
369 pidgin_media_insert_widget(PidginMedia
*gtkmedia
, GtkWidget
*widget
,
370 const gchar
*session_id
, const gchar
*participant
)
372 gchar
*key
= create_key(session_id
, participant
);
373 PurpleMediaSessionType type
=
374 purple_media_get_session_type(gtkmedia
->priv
->media
, session_id
);
376 if (type
& PURPLE_MEDIA_AUDIO
)
377 g_hash_table_insert(gtkmedia
->priv
->recv_progressbars
, key
, widget
);
378 else if (type
& PURPLE_MEDIA_VIDEO
)
379 g_hash_table_insert(gtkmedia
->priv
->remote_videos
, key
, widget
);
383 pidgin_media_get_widget(PidginMedia
*gtkmedia
,
384 const gchar
*session_id
, const gchar
*participant
)
386 GtkWidget
*widget
= NULL
;
387 gchar
*key
= create_key(session_id
, participant
);
388 PurpleMediaSessionType type
=
389 purple_media_get_session_type(gtkmedia
->priv
->media
, session_id
);
391 if (type
& PURPLE_MEDIA_AUDIO
)
392 widget
= g_hash_table_lookup(gtkmedia
->priv
->recv_progressbars
, key
);
393 else if (type
& PURPLE_MEDIA_VIDEO
)
394 widget
= g_hash_table_lookup(gtkmedia
->priv
->remote_videos
, key
);
401 pidgin_media_remove_widget(PidginMedia
*gtkmedia
,
402 const gchar
*session_id
, const gchar
*participant
)
404 GtkWidget
*widget
= pidgin_media_get_widget(gtkmedia
, session_id
, participant
);
407 PurpleMediaSessionType type
=
408 purple_media_get_session_type(gtkmedia
->priv
->media
, session_id
);
409 gchar
*key
= create_key(session_id
, participant
);
412 if (type
& PURPLE_MEDIA_AUDIO
) {
413 g_hash_table_remove(gtkmedia
->priv
->recv_progressbars
, key
);
415 if (g_hash_table_size(gtkmedia
->priv
->recv_progressbars
) == 0 &&
416 gtkmedia
->priv
->send_progress
) {
418 gtk_widget_destroy(gtkmedia
->priv
->send_progress
);
419 gtkmedia
->priv
->send_progress
= NULL
;
421 gtk_widget_destroy(gtkmedia
->priv
->mute
);
422 gtkmedia
->priv
->mute
= NULL
;
424 } else if (type
& PURPLE_MEDIA_VIDEO
) {
425 g_hash_table_remove(gtkmedia
->priv
->remote_videos
, key
);
427 if (g_hash_table_size(gtkmedia
->priv
->remote_videos
) == 0 &&
428 gtkmedia
->priv
->local_video
) {
430 gtk_widget_destroy(gtkmedia
->priv
->local_video
);
431 gtkmedia
->priv
->local_video
= NULL
;
433 gtk_widget_destroy(gtkmedia
->priv
->pause
);
434 gtkmedia
->priv
->pause
= NULL
;
440 gtk_widget_destroy(widget
);
442 gtk_widget_size_request(GTK_WIDGET(gtkmedia
), &req
);
443 gtk_window_resize(GTK_WINDOW(gtkmedia
), req
.width
, req
.height
);
448 level_message_cb(PurpleMedia
*media
, gchar
*session_id
, gchar
*participant
,
449 double level
, PidginMedia
*gtkmedia
)
451 GtkWidget
*progress
= NULL
;
452 if (participant
== NULL
)
453 progress
= gtkmedia
->priv
->send_progress
;
455 progress
= pidgin_media_get_widget(gtkmedia
, session_id
, participant
);
458 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(progress
), level
* 5);
463 pidgin_media_disconnect_levels(PurpleMedia
*media
, PidginMedia
*gtkmedia
)
465 PurpleMediaManager
*manager
= purple_media_get_manager(media
);
466 GstElement
*element
= purple_media_manager_get_pipeline(manager
);
467 gulong handler_id
= g_signal_handler_find(G_OBJECT(gst_pipeline_get_bus(GST_PIPELINE(element
))),
468 G_SIGNAL_MATCH_FUNC
| G_SIGNAL_MATCH_DATA
, 0, 0,
469 NULL
, G_CALLBACK(level_message_cb
), gtkmedia
);
471 g_signal_handler_disconnect(G_OBJECT(gst_pipeline_get_bus(GST_PIPELINE(element
))),
476 pidgin_media_dispose(GObject
*media
)
478 PidginMedia
*gtkmedia
= PIDGIN_MEDIA(media
);
479 purple_debug_info("gtkmedia", "pidgin_media_dispose\n");
481 if (gtkmedia
->priv
->media
) {
482 purple_request_close_with_handle(gtkmedia
);
483 purple_media_remove_output_windows(gtkmedia
->priv
->media
);
484 pidgin_media_disconnect_levels(gtkmedia
->priv
->media
, gtkmedia
);
485 g_object_unref(gtkmedia
->priv
->media
);
486 gtkmedia
->priv
->media
= NULL
;
489 if (gtkmedia
->priv
->ui
) {
490 g_object_unref(gtkmedia
->priv
->ui
);
491 gtkmedia
->priv
->ui
= NULL
;
494 if (gtkmedia
->priv
->timeout_id
!= 0)
495 g_source_remove(gtkmedia
->priv
->timeout_id
);
497 if (gtkmedia
->priv
->recv_progressbars
) {
498 g_hash_table_destroy(gtkmedia
->priv
->recv_progressbars
);
499 g_hash_table_destroy(gtkmedia
->priv
->remote_videos
);
500 gtkmedia
->priv
->recv_progressbars
= NULL
;
501 gtkmedia
->priv
->remote_videos
= NULL
;
504 G_OBJECT_CLASS(parent_class
)->dispose(media
);
508 pidgin_media_finalize(GObject
*media
)
510 /* PidginMedia *gtkmedia = PIDGIN_MEDIA(media); */
511 purple_debug_info("gtkmedia", "pidgin_media_finalize\n");
513 G_OBJECT_CLASS(parent_class
)->finalize(media
);
517 pidgin_media_emit_message(PidginMedia
*gtkmedia
, const char *msg
)
519 PurpleConversation
*conv
= purple_find_conversation_with_account(
520 PURPLE_CONV_TYPE_ANY
, gtkmedia
->priv
->screenname
,
521 purple_media_get_account(gtkmedia
->priv
->media
));
523 purple_conversation_write(conv
, NULL
, msg
,
524 PURPLE_MESSAGE_SYSTEM
, time(NULL
));
529 PidginMedia
*gtkmedia
;
532 } PidginMediaRealizeData
;
535 realize_cb_cb(PidginMediaRealizeData
*data
)
537 PidginMediaPrivate
*priv
= data
->gtkmedia
->priv
;
538 GdkWindow
*window
= NULL
;
540 if (data
->participant
== NULL
)
541 #if GTK_CHECK_VERSION(2, 14, 0)
542 window
= gtk_widget_get_window(priv
->local_video
);
544 window
= (priv
->local_video
)->window
;
547 GtkWidget
*widget
= pidgin_media_get_widget(data
->gtkmedia
,
548 data
->session_id
, data
->participant
);
550 #if GTK_CHECK_VERSION(2, 14, 0)
551 window
= gtk_widget_get_window(widget
);
553 window
= widget
->window
;
560 window_id
= GDK_WINDOW_HWND(window
);
561 #elif defined(HAVE_X11)
562 window_id
= GDK_WINDOW_XWINDOW(window
);
564 # error "Unsupported windowing system"
567 purple_media_set_output_window(priv
->media
, data
->session_id
,
568 data
->participant
, window_id
);
571 g_free(data
->session_id
);
572 g_free(data
->participant
);
578 realize_cb(GtkWidget
*widget
, PidginMediaRealizeData
*data
)
580 g_timeout_add(0, (GSourceFunc
)realize_cb_cb
, data
);
584 pidgin_media_error_cb(PidginMedia
*media
, const char *error
, PidginMedia
*gtkmedia
)
586 PurpleConversation
*conv
= purple_find_conversation_with_account(
587 PURPLE_CONV_TYPE_ANY
, gtkmedia
->priv
->screenname
,
588 purple_media_get_account(gtkmedia
->priv
->media
));
590 purple_conversation_write(conv
, NULL
, error
,
591 PURPLE_MESSAGE_ERROR
, time(NULL
));
592 gtk_statusbar_push(GTK_STATUSBAR(gtkmedia
->priv
->statusbar
),
597 pidgin_media_accept_cb(PurpleMedia
*media
, int index
)
599 purple_media_stream_info(media
, PURPLE_MEDIA_INFO_ACCEPT
,
604 pidgin_media_reject_cb(PurpleMedia
*media
, int index
)
606 GList
*iter
= purple_media_get_session_ids(media
);
607 for (; iter
; iter
= g_list_delete_link(iter
, iter
)) {
608 const gchar
*sessionid
= iter
->data
;
609 if (!purple_media_accepted(media
, sessionid
, NULL
))
610 purple_media_stream_info(media
, PURPLE_MEDIA_INFO_REJECT
,
611 sessionid
, NULL
, TRUE
);
616 pidgin_request_timeout_cb(PidginMedia
*gtkmedia
)
618 PurpleAccount
*account
;
621 PurpleMediaSessionType type
;
622 gchar
*message
= NULL
;
624 account
= purple_media_get_account(gtkmedia
->priv
->media
);
625 buddy
= purple_find_buddy(account
, gtkmedia
->priv
->screenname
);
626 alias
= buddy
? purple_buddy_get_contact_alias(buddy
) :
627 gtkmedia
->priv
->screenname
;
628 type
= gtkmedia
->priv
->request_type
;
629 gtkmedia
->priv
->timeout_id
= 0;
631 if (type
& PURPLE_MEDIA_AUDIO
&& type
& PURPLE_MEDIA_VIDEO
) {
632 message
= g_strdup_printf(_("%s wishes to start an audio/video session with you."),
634 } else if (type
& PURPLE_MEDIA_AUDIO
) {
635 message
= g_strdup_printf(_("%s wishes to start an audio session with you."),
637 } else if (type
& PURPLE_MEDIA_VIDEO
) {
638 message
= g_strdup_printf(_("%s wishes to start a video session with you."),
642 gtkmedia
->priv
->request_type
= PURPLE_MEDIA_NONE
;
643 if (!purple_media_accepted(gtkmedia
->priv
->media
, NULL
, NULL
)) {
644 purple_request_accept_cancel(gtkmedia
, _("Incoming Call"),
645 message
, NULL
, PURPLE_DEFAULT_ACTION_NONE
,
646 (void*)account
, gtkmedia
->priv
->screenname
,
647 NULL
, gtkmedia
->priv
->media
,
648 pidgin_media_accept_cb
,
649 pidgin_media_reject_cb
);
651 pidgin_media_emit_message(gtkmedia
, message
);
657 #if GTK_CHECK_VERSION(2,12,0)
658 pidgin_media_input_volume_changed(GtkScaleButton
*range
, double value
,
661 double val
= (double)value
* 100.0;
663 pidgin_media_input_volume_changed(GtkRange
*range
, PurpleMedia
*media
)
665 double val
= (double)gtk_range_get_value(GTK_RANGE(range
));
667 purple_media_set_input_volume(media
, NULL
, val
);
671 #if GTK_CHECK_VERSION(2,12,0)
672 pidgin_media_output_volume_changed(GtkScaleButton
*range
, double value
,
675 double val
= (double)value
* 100.0;
677 pidgin_media_output_volume_changed(GtkRange
*range
, PurpleMedia
*media
)
679 double val
= (double)gtk_range_get_value(GTK_RANGE(range
));
681 purple_media_set_output_volume(media
, NULL
, NULL
, val
);
685 destroy_parent_widget_cb(GtkWidget
*widget
, GtkWidget
*parent
)
687 g_return_if_fail(GTK_IS_WIDGET(parent
));
689 gtk_widget_destroy(parent
);
693 pidgin_media_add_audio_widget(PidginMedia
*gtkmedia
,
694 PurpleMediaSessionType type
, const gchar
*sid
)
696 GtkWidget
*volume_widget
, *progress_parent
, *volume
, *progress
;
699 if (type
& PURPLE_MEDIA_SEND_AUDIO
) {
700 value
= purple_prefs_get_int(
701 "/purple/media/audio/volume/input");
702 } else if (type
& PURPLE_MEDIA_RECV_AUDIO
) {
703 value
= purple_prefs_get_int(
704 "/purple/media/audio/volume/output");
706 g_return_val_if_reached(NULL
);
708 #if GTK_CHECK_VERSION(2,12,0)
709 /* Setup widget structure */
710 volume_widget
= gtk_hbox_new(FALSE
, PIDGIN_HIG_BOX_SPACE
);
711 progress_parent
= gtk_vbox_new(FALSE
, 0);
712 gtk_box_pack_start(GTK_BOX(volume_widget
),
713 progress_parent
, TRUE
, TRUE
, 0);
716 volume
= gtk_volume_button_new();
717 gtk_scale_button_set_value(GTK_SCALE_BUTTON(volume
), value
/100.0);
718 gtk_box_pack_end(GTK_BOX(volume_widget
),
719 volume
, FALSE
, FALSE
, 0);
721 /* Setup widget structure */
722 volume_widget
= gtk_vbox_new(FALSE
, 0);
723 progress_parent
= volume_widget
;
726 volume
= gtk_hscale_new_with_range(0.0, 100.0, 5.0);
727 gtk_range_set_increments(GTK_RANGE(volume
), 5.0, 25.0);
728 gtk_range_set_value(GTK_RANGE(volume
), value
);
729 gtk_scale_set_draw_value(GTK_SCALE(volume
), FALSE
);
730 gtk_box_pack_end(GTK_BOX(volume_widget
),
731 volume
, TRUE
, FALSE
, 0);
734 /* Volume level indicator */
735 progress
= gtk_progress_bar_new();
736 gtk_widget_set_size_request(progress
, 250, 10);
737 gtk_box_pack_end(GTK_BOX(progress_parent
), progress
, TRUE
, FALSE
, 0);
739 if (type
& PURPLE_MEDIA_SEND_AUDIO
) {
740 g_signal_connect (G_OBJECT(volume
), "value-changed",
741 G_CALLBACK(pidgin_media_input_volume_changed
),
742 gtkmedia
->priv
->media
);
743 gtkmedia
->priv
->send_progress
= progress
;
744 } else if (type
& PURPLE_MEDIA_RECV_AUDIO
) {
745 g_signal_connect (G_OBJECT(volume
), "value-changed",
746 G_CALLBACK(pidgin_media_output_volume_changed
),
747 gtkmedia
->priv
->media
);
749 pidgin_media_insert_widget(gtkmedia
, progress
, sid
, gtkmedia
->priv
->screenname
);
752 g_signal_connect(G_OBJECT(progress
), "destroy",
753 G_CALLBACK(destroy_parent_widget_cb
),
756 gtk_widget_show_all(volume_widget
);
758 return volume_widget
;
762 pidgin_media_ready_cb(PurpleMedia
*media
, PidginMedia
*gtkmedia
, const gchar
*sid
)
764 GtkWidget
*send_widget
= NULL
, *recv_widget
= NULL
, *button_widget
= NULL
;
765 PurpleMediaSessionType type
=
766 purple_media_get_session_type(media
, sid
);
767 GdkPixbuf
*icon
= NULL
;
769 if (gtkmedia
->priv
->recv_widget
== NULL
770 && type
& (PURPLE_MEDIA_RECV_VIDEO
|
771 PURPLE_MEDIA_RECV_AUDIO
)) {
772 recv_widget
= gtk_vbox_new(FALSE
, PIDGIN_HIG_BOX_SPACE
);
773 gtk_box_pack_start(GTK_BOX(gtkmedia
->priv
->display
),
774 recv_widget
, TRUE
, TRUE
, 0);
775 gtk_widget_show(recv_widget
);
777 recv_widget
= gtkmedia
->priv
->recv_widget
;
779 if (gtkmedia
->priv
->send_widget
== NULL
780 && type
& (PURPLE_MEDIA_SEND_VIDEO
|
781 PURPLE_MEDIA_SEND_AUDIO
)) {
782 send_widget
= gtk_vbox_new(FALSE
, PIDGIN_HIG_BOX_SPACE
);
783 gtk_box_pack_start(GTK_BOX(gtkmedia
->priv
->display
),
784 send_widget
, FALSE
, TRUE
, 0);
785 button_widget
= gtk_hbox_new(FALSE
, PIDGIN_HIG_BOX_SPACE
);
786 gtk_box_pack_end(GTK_BOX(recv_widget
), button_widget
,
788 gtk_widget_show(send_widget
);
791 gtkmedia
->priv
->hold
=
792 gtk_toggle_button_new_with_mnemonic("_Hold");
793 gtk_box_pack_end(GTK_BOX(button_widget
), gtkmedia
->priv
->hold
,
795 gtk_widget_show(gtkmedia
->priv
->hold
);
796 g_signal_connect(gtkmedia
->priv
->hold
, "toggled",
797 G_CALLBACK(pidgin_media_hold_toggled
),
800 send_widget
= gtkmedia
->priv
->send_widget
;
801 button_widget
= gtkmedia
->priv
->button_widget
;
804 if (type
& PURPLE_MEDIA_RECV_VIDEO
) {
805 PidginMediaRealizeData
*data
;
807 GtkWidget
*remote_video
;
808 GdkColor color
= {0, 0, 0, 0};
810 aspect
= gtk_aspect_frame_new(NULL
, 0, 0, 4.0/3.0, FALSE
);
811 gtk_frame_set_shadow_type(GTK_FRAME(aspect
), GTK_SHADOW_IN
);
812 gtk_box_pack_start(GTK_BOX(recv_widget
), aspect
, TRUE
, TRUE
, 0);
814 data
= g_new0(PidginMediaRealizeData
, 1);
815 data
->gtkmedia
= gtkmedia
;
816 data
->session_id
= g_strdup(sid
);
817 data
->participant
= g_strdup(gtkmedia
->priv
->screenname
);
819 remote_video
= gtk_drawing_area_new();
820 gtk_widget_modify_bg(remote_video
, GTK_STATE_NORMAL
, &color
);
821 g_signal_connect(G_OBJECT(remote_video
), "realize",
822 G_CALLBACK(realize_cb
), data
);
823 gtk_container_add(GTK_CONTAINER(aspect
), remote_video
);
824 gtk_widget_set_size_request (GTK_WIDGET(remote_video
), 320, 240);
825 g_signal_connect(G_OBJECT(remote_video
), "destroy",
826 G_CALLBACK(destroy_parent_widget_cb
), aspect
);
828 gtk_widget_show(remote_video
);
829 gtk_widget_show(aspect
);
831 pidgin_media_insert_widget(gtkmedia
, remote_video
,
832 data
->session_id
, data
->participant
);
835 if (type
& PURPLE_MEDIA_SEND_VIDEO
&& !gtkmedia
->priv
->local_video
) {
836 PidginMediaRealizeData
*data
;
838 GtkWidget
*local_video
;
839 GdkColor color
= {0, 0, 0, 0};
841 aspect
= gtk_aspect_frame_new(NULL
, 0, 0, 4.0/3.0, TRUE
);
842 gtk_frame_set_shadow_type(GTK_FRAME(aspect
), GTK_SHADOW_IN
);
843 gtk_box_pack_start(GTK_BOX(send_widget
), aspect
, FALSE
, TRUE
, 0);
845 data
= g_new0(PidginMediaRealizeData
, 1);
846 data
->gtkmedia
= gtkmedia
;
847 data
->session_id
= g_strdup(sid
);
848 data
->participant
= NULL
;
850 local_video
= gtk_drawing_area_new();
851 gtk_widget_modify_bg(local_video
, GTK_STATE_NORMAL
, &color
);
852 g_signal_connect(G_OBJECT(local_video
), "realize",
853 G_CALLBACK(realize_cb
), data
);
854 gtk_container_add(GTK_CONTAINER(aspect
), local_video
);
855 gtk_widget_set_size_request (GTK_WIDGET(local_video
), 80, 60);
856 g_signal_connect(G_OBJECT(local_video
), "destroy",
857 G_CALLBACK(destroy_parent_widget_cb
), aspect
);
859 gtk_widget_show(local_video
);
860 gtk_widget_show(aspect
);
862 gtkmedia
->priv
->pause
=
863 gtk_toggle_button_new_with_mnemonic(_("_Pause"));
864 gtk_box_pack_end(GTK_BOX(button_widget
), gtkmedia
->priv
->pause
,
866 gtk_widget_show(gtkmedia
->priv
->pause
);
867 g_signal_connect(gtkmedia
->priv
->pause
, "toggled",
868 G_CALLBACK(pidgin_media_pause_toggled
),
871 gtkmedia
->priv
->local_video
= local_video
;
873 if (type
& PURPLE_MEDIA_RECV_AUDIO
) {
874 gtk_box_pack_end(GTK_BOX(recv_widget
),
875 pidgin_media_add_audio_widget(gtkmedia
,
876 PURPLE_MEDIA_RECV_AUDIO
, sid
), FALSE
, FALSE
, 0);
879 if (type
& PURPLE_MEDIA_SEND_AUDIO
) {
880 gtkmedia
->priv
->mute
=
881 gtk_toggle_button_new_with_mnemonic("_Mute");
882 gtk_box_pack_end(GTK_BOX(button_widget
), gtkmedia
->priv
->mute
,
884 gtk_widget_show(gtkmedia
->priv
->mute
);
885 g_signal_connect(gtkmedia
->priv
->mute
, "toggled",
886 G_CALLBACK(pidgin_media_mute_toggled
),
889 gtk_box_pack_end(GTK_BOX(recv_widget
),
890 pidgin_media_add_audio_widget(gtkmedia
,
891 PURPLE_MEDIA_SEND_AUDIO
, NULL
), FALSE
, FALSE
, 0);
894 if (type
& PURPLE_MEDIA_AUDIO
&&
895 gtkmedia
->priv
->level_handler_id
== 0) {
896 gtkmedia
->priv
->level_handler_id
= g_signal_connect(
897 media
, "level", G_CALLBACK(level_message_cb
),
901 if (send_widget
!= NULL
)
902 gtkmedia
->priv
->send_widget
= send_widget
;
903 if (recv_widget
!= NULL
)
904 gtkmedia
->priv
->recv_widget
= recv_widget
;
905 if (button_widget
!= NULL
) {
906 gtkmedia
->priv
->button_widget
= button_widget
;
907 gtk_widget_show(GTK_WIDGET(button_widget
));
910 if (purple_media_is_initiator(media
, sid
, NULL
) == FALSE
) {
911 if (gtkmedia
->priv
->timeout_id
!= 0)
912 g_source_remove(gtkmedia
->priv
->timeout_id
);
913 gtkmedia
->priv
->request_type
|= type
;
914 gtkmedia
->priv
->timeout_id
= g_timeout_add(500,
915 (GSourceFunc
)pidgin_request_timeout_cb
,
919 /* set the window icon according to the type */
920 if (type
& PURPLE_MEDIA_VIDEO
) {
921 icon
= gtk_widget_render_icon(GTK_WIDGET(gtkmedia
),
922 PIDGIN_STOCK_TOOLBAR_VIDEO_CALL
,
923 gtk_icon_size_from_name(PIDGIN_ICON_SIZE_TANGO_LARGE
), NULL
);
924 } else if (type
& PURPLE_MEDIA_AUDIO
) {
925 icon
= gtk_widget_render_icon(GTK_WIDGET(gtkmedia
),
926 PIDGIN_STOCK_TOOLBAR_AUDIO_CALL
,
927 gtk_icon_size_from_name(PIDGIN_ICON_SIZE_TANGO_LARGE
), NULL
);
931 gtk_window_set_icon(GTK_WINDOW(gtkmedia
), icon
);
932 g_object_unref(icon
);
935 gtk_widget_show(gtkmedia
->priv
->display
);
939 pidgin_media_state_changed_cb(PurpleMedia
*media
, PurpleMediaState state
,
940 gchar
*sid
, gchar
*name
, PidginMedia
*gtkmedia
)
942 purple_debug_info("gtkmedia", "state: %d sid: %s name: %s\n",
943 state
, sid
? sid
: "(null)", name
? name
: "(null)");
944 if (state
== PURPLE_MEDIA_STATE_END
) {
945 if (sid
!= NULL
&& name
!= NULL
) {
946 pidgin_media_remove_widget(gtkmedia
, sid
, name
);
947 } else if (sid
== NULL
&& name
== NULL
) {
948 pidgin_media_emit_message(gtkmedia
,
949 _("The call has been terminated."));
950 gtk_widget_destroy(GTK_WIDGET(gtkmedia
));
952 } else if (state
== PURPLE_MEDIA_STATE_NEW
&&
953 sid
!= NULL
&& name
!= NULL
) {
954 pidgin_media_ready_cb(media
, gtkmedia
, sid
);
959 pidgin_media_stream_info_cb(PurpleMedia
*media
, PurpleMediaInfoType type
,
960 gchar
*sid
, gchar
*name
, gboolean local
,
961 PidginMedia
*gtkmedia
)
963 if (type
== PURPLE_MEDIA_INFO_REJECT
) {
964 pidgin_media_emit_message(gtkmedia
,
965 _("You have rejected the call."));
966 } else if (type
== PURPLE_MEDIA_INFO_ACCEPT
) {
968 purple_request_close_with_handle(gtkmedia
);
969 pidgin_media_set_state(gtkmedia
, PIDGIN_MEDIA_ACCEPTED
);
970 pidgin_media_emit_message(gtkmedia
, _("Call in progress."));
971 gtk_statusbar_push(GTK_STATUSBAR(gtkmedia
->priv
->statusbar
),
972 0, _("Call in progress."));
973 gtk_widget_show(GTK_WIDGET(gtkmedia
));
978 pidgin_media_set_property (GObject
*object
, guint prop_id
, const GValue
*value
, GParamSpec
*pspec
)
981 g_return_if_fail(PIDGIN_IS_MEDIA(object
));
983 media
= PIDGIN_MEDIA(object
);
987 if (media
->priv
->media
)
988 g_object_unref(media
->priv
->media
);
989 media
->priv
->media
= g_value_get_object(value
);
990 g_object_ref(media
->priv
->media
);
992 if (purple_media_is_initiator(media
->priv
->media
,
994 pidgin_media_set_state(media
, PIDGIN_MEDIA_WAITING
);
996 pidgin_media_set_state(media
, PIDGIN_MEDIA_REQUESTED
);
998 g_signal_connect(G_OBJECT(media
->priv
->media
), "error",
999 G_CALLBACK(pidgin_media_error_cb
), media
);
1000 g_signal_connect(G_OBJECT(media
->priv
->media
), "state-changed",
1001 G_CALLBACK(pidgin_media_state_changed_cb
), media
);
1002 g_signal_connect(G_OBJECT(media
->priv
->media
), "stream-info",
1003 G_CALLBACK(pidgin_media_stream_info_cb
), media
);
1006 case PROP_SCREENNAME
:
1007 if (media
->priv
->screenname
)
1008 g_free(media
->priv
->screenname
);
1009 media
->priv
->screenname
= g_value_dup_string(value
);
1012 G_OBJECT_WARN_INVALID_PROPERTY_ID (object
, prop_id
, pspec
);
1018 pidgin_media_get_property (GObject
*object
, guint prop_id
, GValue
*value
, GParamSpec
*pspec
)
1021 g_return_if_fail(PIDGIN_IS_MEDIA(object
));
1023 media
= PIDGIN_MEDIA(object
);
1027 g_value_set_object(value
, media
->priv
->media
);
1029 case PROP_SCREENNAME
:
1030 g_value_set_string(value
, media
->priv
->screenname
);
1033 G_OBJECT_WARN_INVALID_PROPERTY_ID (object
, prop_id
, pspec
);
1039 pidgin_media_new(PurpleMedia
*media
, const gchar
*screenname
)
1041 PidginMedia
*gtkmedia
= g_object_new(pidgin_media_get_type(),
1043 "screenname", screenname
, NULL
);
1044 return GTK_WIDGET(gtkmedia
);
1048 pidgin_media_set_state(PidginMedia
*gtkmedia
, PidginMediaState state
)
1050 gtkmedia
->priv
->state
= state
;
1054 pidgin_media_new_cb(PurpleMediaManager
*manager
, PurpleMedia
*media
,
1055 PurpleAccount
*account
, gchar
*screenname
, gpointer nul
)
1057 PidginMedia
*gtkmedia
= PIDGIN_MEDIA(
1058 pidgin_media_new(media
, screenname
));
1059 PurpleBuddy
*buddy
= purple_find_buddy(account
, screenname
);
1060 const gchar
*alias
= buddy
?
1061 purple_buddy_get_contact_alias(buddy
) : screenname
;
1062 gtk_window_set_title(GTK_WINDOW(gtkmedia
), alias
);
1064 if (purple_media_is_initiator(media
, NULL
, NULL
) == TRUE
)
1065 gtk_widget_show(GTK_WIDGET(gtkmedia
));
1071 create_default_video_src(PurpleMedia
*media
,
1072 const gchar
*session_id
, const gchar
*participant
)
1074 GstElement
*sendbin
, *src
;
1079 /* autovideosrc doesn't pick ksvideosrc for some reason */
1080 src
= gst_element_factory_make("ksvideosrc", NULL
);
1082 src
= gst_element_factory_make("dshowvideosrc", NULL
);
1084 src
= gst_element_factory_make("autovideosrc", NULL
);
1086 src
= gst_element_factory_make("gconfvideosrc", NULL
);
1088 src
= gst_element_factory_make("autovideosrc", NULL
);
1090 src
= gst_element_factory_make("v4l2src", NULL
);
1092 src
= gst_element_factory_make("v4lsrc", NULL
);
1095 purple_debug_error("gtkmedia", "Unable to find a suitable "
1096 "element for the default video source.\n");
1100 sendbin
= gst_bin_new("pidgindefaultvideosrc");
1102 gst_bin_add(GST_BIN(sendbin
), src
);
1104 pad
= gst_element_get_static_pad(src
, "src");
1105 ghost
= gst_ghost_pad_new("ghostsrc", pad
);
1106 gst_object_unref(pad
);
1107 gst_element_add_pad(sendbin
, ghost
);
1113 create_default_video_sink(PurpleMedia
*media
,
1114 const gchar
*session_id
, const gchar
*participant
)
1116 GstElement
*sink
= gst_element_factory_make("gconfvideosink", NULL
);
1118 sink
= gst_element_factory_make("autovideosink", NULL
);
1120 purple_debug_error("gtkmedia", "Unable to find a suitable "
1121 "element for the default video sink.\n");
1126 create_default_audio_src(PurpleMedia
*media
,
1127 const gchar
*session_id
, const gchar
*participant
)
1130 src
= gst_element_factory_make("gconfaudiosrc", NULL
);
1132 src
= gst_element_factory_make("autoaudiosrc", NULL
);
1134 src
= gst_element_factory_make("alsasrc", NULL
);
1136 src
= gst_element_factory_make("osssrc", NULL
);
1138 src
= gst_element_factory_make("dshowaudiosrc", NULL
);
1140 purple_debug_error("gtkmedia", "Unable to find a suitable "
1141 "element for the default audio source.\n");
1144 gst_element_set_name(src
, "pidgindefaultaudiosrc");
1149 create_default_audio_sink(PurpleMedia
*media
,
1150 const gchar
*session_id
, const gchar
*participant
)
1153 sink
= gst_element_factory_make("gconfaudiosink", NULL
);
1155 sink
= gst_element_factory_make("autoaudiosink",NULL
);
1157 purple_debug_error("gtkmedia", "Unable to find a suitable "
1158 "element for the default audio sink.\n");
1166 pidgin_medias_init(void)
1169 PurpleMediaManager
*manager
= purple_media_manager_get();
1170 PurpleMediaElementInfo
*default_video_src
=
1171 g_object_new(PURPLE_TYPE_MEDIA_ELEMENT_INFO
,
1172 "id", "pidgindefaultvideosrc",
1173 "name", "Pidgin Default Video Source",
1174 "type", PURPLE_MEDIA_ELEMENT_VIDEO
1175 | PURPLE_MEDIA_ELEMENT_SRC
1176 | PURPLE_MEDIA_ELEMENT_ONE_SRC
1177 | PURPLE_MEDIA_ELEMENT_UNIQUE
,
1178 "create-cb", create_default_video_src
, NULL
);
1179 PurpleMediaElementInfo
*default_video_sink
=
1180 g_object_new(PURPLE_TYPE_MEDIA_ELEMENT_INFO
,
1181 "id", "pidgindefaultvideosink",
1182 "name", "Pidgin Default Video Sink",
1183 "type", PURPLE_MEDIA_ELEMENT_VIDEO
1184 | PURPLE_MEDIA_ELEMENT_SINK
1185 | PURPLE_MEDIA_ELEMENT_ONE_SINK
,
1186 "create-cb", create_default_video_sink
, NULL
);
1187 PurpleMediaElementInfo
*default_audio_src
=
1188 g_object_new(PURPLE_TYPE_MEDIA_ELEMENT_INFO
,
1189 "id", "pidgindefaultaudiosrc",
1190 "name", "Pidgin Default Audio Source",
1191 "type", PURPLE_MEDIA_ELEMENT_AUDIO
1192 | PURPLE_MEDIA_ELEMENT_SRC
1193 | PURPLE_MEDIA_ELEMENT_ONE_SRC
1194 | PURPLE_MEDIA_ELEMENT_UNIQUE
,
1195 "create-cb", create_default_audio_src
, NULL
);
1196 PurpleMediaElementInfo
*default_audio_sink
=
1197 g_object_new(PURPLE_TYPE_MEDIA_ELEMENT_INFO
,
1198 "id", "pidgindefaultaudiosink",
1199 "name", "Pidgin Default Audio Sink",
1200 "type", PURPLE_MEDIA_ELEMENT_AUDIO
1201 | PURPLE_MEDIA_ELEMENT_SINK
1202 | PURPLE_MEDIA_ELEMENT_ONE_SINK
,
1203 "create-cb", create_default_audio_sink
, NULL
);
1205 g_signal_connect(G_OBJECT(manager
), "init-media",
1206 G_CALLBACK(pidgin_media_new_cb
), NULL
);
1208 purple_media_manager_set_ui_caps(manager
,
1209 PURPLE_MEDIA_CAPS_AUDIO
|
1210 PURPLE_MEDIA_CAPS_AUDIO_SINGLE_DIRECTION
|
1211 PURPLE_MEDIA_CAPS_VIDEO
|
1212 PURPLE_MEDIA_CAPS_VIDEO_SINGLE_DIRECTION
|
1213 PURPLE_MEDIA_CAPS_AUDIO_VIDEO
);
1215 purple_debug_info("gtkmedia", "Registering media element types\n");
1216 purple_media_manager_set_active_element(manager
, default_video_src
);
1217 purple_media_manager_set_active_element(manager
, default_video_sink
);
1218 purple_media_manager_set_active_element(manager
, default_audio_src
);
1219 purple_media_manager_set_active_element(manager
, default_audio_sink
);