* @file media.c Account API * Pidgin is the legal property of its developers, whose names are too numerous * to list here. Please refer to the COPYRIGHT file distributed with this * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA #include "mediamanager.h" #ifdef GDK_WINDOWING_WIN32 #include <gdk/gdkwin32.h> #ifdef GDK_WINDOWING_QUARTZ #include <gdk/gdkquartz.h> #define PIDGIN_TYPE_MEDIA (pidgin_media_get_type()) #define PIDGIN_MEDIA(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), PIDGIN_TYPE_MEDIA, PidginMedia)) #define PIDGIN_MEDIA_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), PIDGIN_TYPE_MEDIA, PidginMediaClass)) #define PIDGIN_IS_MEDIA(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), PIDGIN_TYPE_MEDIA)) #define PIDGIN_IS_MEDIA_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), PIDGIN_TYPE_MEDIA)) #define PIDGIN_MEDIA_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), PIDGIN_TYPE_MEDIA, PidginMediaClass)) typedef struct _PidginMedia PidginMedia; typedef struct _PidginMediaClass PidginMediaClass; typedef struct _PidginMediaPrivate PidginMediaPrivate; /* Waiting for response */ PIDGIN_MEDIA_WAITING = 1, GtkWindowClass parent_class; PidginMediaPrivate *priv; struct _PidginMediaPrivate GtkWidget *send_progress; GHashTable *recv_progressbars; GtkWidget *button_widget; GHashTable *remote_videos; PurpleMediaSessionType request_type; #define PIDGIN_MEDIA_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE((obj), PIDGIN_TYPE_MEDIA, PidginMediaPrivate)) static void pidgin_media_class_init (PidginMediaClass *klass); static void pidgin_media_init (PidginMedia *media); static void pidgin_media_dispose (GObject *object); static void pidgin_media_finalize (GObject *object); static void pidgin_media_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec); static void pidgin_media_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec); static void pidgin_media_set_state(PidginMedia *gtkmedia, PidginMediaState state); static GtkWindowClass *parent_class = NULL; static guint pidgin_media_signals[LAST_SIGNAL] = {0}; pidgin_media_get_type(void) static const GTypeInfo info = { sizeof(PidginMediaClass), (GClassInitFunc) pidgin_media_class_init, (GInstanceInitFunc) pidgin_media_init, type = g_type_register_static(GTK_TYPE_WINDOW, "PidginMedia", &info, 0); pidgin_media_class_init (PidginMediaClass *klass) GObjectClass *gobject_class = (GObjectClass*)klass; /* GtkContainerClass *container_class = (GtkContainerClass*)klass; */ parent_class = g_type_class_peek_parent(klass); gobject_class->dispose = pidgin_media_dispose; gobject_class->finalize = pidgin_media_finalize; gobject_class->set_property = pidgin_media_set_property; gobject_class->get_property = pidgin_media_get_property; g_object_class_install_property(gobject_class, PROP_MEDIA, g_param_spec_object("media", "The PurpleMedia associated with this media.", G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); g_object_class_install_property(gobject_class, PROP_SCREENNAME, g_param_spec_string("screenname", "The screenname of the user this session is with.", G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); g_type_class_add_private(klass, sizeof(PidginMediaPrivate)); pidgin_media_hold_toggled(GtkToggleButton *toggle, PidginMedia *media) purple_media_stream_info(media->priv->media, gtk_toggle_button_get_active(toggle) ? PURPLE_MEDIA_INFO_HOLD : PURPLE_MEDIA_INFO_UNHOLD, pidgin_media_mute_toggled(GtkToggleButton *toggle, PidginMedia *media) purple_media_stream_info(media->priv->media, gtk_toggle_button_get_active(toggle) ? PURPLE_MEDIA_INFO_MUTE : PURPLE_MEDIA_INFO_UNMUTE, pidgin_media_pause_toggled(GtkToggleButton *toggle, PidginMedia *media) purple_media_stream_info(media->priv->media, gtk_toggle_button_get_active(toggle) ? PURPLE_MEDIA_INFO_PAUSE : PURPLE_MEDIA_INFO_UNPAUSE, pidgin_media_delete_event_cb(GtkWidget *widget, GdkEvent *event, PidginMedia *media) purple_media_stream_info(media->priv->media, PURPLE_MEDIA_INFO_HANGUP, NULL, NULL, TRUE); pidgin_x_error_handler(Display *display, XErrorEvent *event) switch (event->error_code) { #define XERRORCASE(type) case type: error_type = #type; break XERRORCASE(BadImplementation); purple_debug_error("media", "A %s Xlib error has occurred. " "The program would normally crash now.\n", menu_hangup(GtkAction *action, gpointer data) PidginMedia *gtkmedia = PIDGIN_MEDIA(data); purple_media_stream_info(gtkmedia->priv->media, PURPLE_MEDIA_INFO_HANGUP, NULL, NULL, TRUE); static const GtkActionEntry menu_entries[] = { { "MediaMenu", NULL, N_("_Media"), NULL, NULL, NULL }, { "Hangup", NULL, N_("_Hangup"), NULL, NULL, G_CALLBACK(menu_hangup) }, static const char *media_menu = "<menu action='MediaMenu'>" "<menuitem action='Hangup'/>" setup_menubar(PidginMedia *window) GtkActionGroup *action_group; GtkAccelGroup *accel_group; action_group = gtk_action_group_new("MediaActions"); gtk_action_group_set_translation_domain(action_group, gtk_action_group_add_actions(action_group, G_N_ELEMENTS(menu_entries), window->priv->ui = gtk_ui_manager_new(); gtk_ui_manager_insert_action_group(window->priv->ui, action_group, 0); accel_group = gtk_ui_manager_get_accel_group(window->priv->ui); gtk_window_add_accel_group(GTK_WINDOW(window), accel_group); if (!gtk_ui_manager_add_ui_from_string(window->priv->ui, media_menu, -1, &error)) g_message("building menus failed: %s", error->message); menu = gtk_ui_manager_get_widget(window->priv->ui, "/Media"); pidgin_media_init (PidginMedia *media) media->priv = PIDGIN_MEDIA_GET_PRIVATE(media); XSetErrorHandler(pidgin_x_error_handler); vbox = gtk_vbox_new(FALSE, 0); gtk_container_add(GTK_CONTAINER(media), vbox); media->priv->statusbar = gtk_statusbar_new(); gtk_box_pack_end(GTK_BOX(vbox), media->priv->statusbar, gtk_statusbar_push(GTK_STATUSBAR(media->priv->statusbar), gtk_widget_show(media->priv->statusbar); media->priv->menubar = setup_menubar(media); gtk_box_pack_start(GTK_BOX(vbox), media->priv->menubar, media->priv->display = gtk_hbox_new(FALSE, PIDGIN_HIG_BOX_SPACE); gtk_container_set_border_width(GTK_CONTAINER(media->priv->display), gtk_box_pack_start(GTK_BOX(vbox), media->priv->display, TRUE, TRUE, PIDGIN_HIG_BOX_SPACE); g_signal_connect(G_OBJECT(media), "delete-event", G_CALLBACK(pidgin_media_delete_event_cb), media); media->priv->recv_progressbars = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL); media->priv->remote_videos = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL); create_key(const gchar *session_id, const gchar *participant) return g_strdup_printf("%s_%s", session_id, participant); pidgin_media_insert_widget(PidginMedia *gtkmedia, GtkWidget *widget, const gchar *session_id, const gchar *participant) gchar *key = create_key(session_id, participant); PurpleMediaSessionType type = purple_media_get_session_type(gtkmedia->priv->media, session_id); if (type & PURPLE_MEDIA_AUDIO) g_hash_table_insert(gtkmedia->priv->recv_progressbars, key, widget); else if (type & PURPLE_MEDIA_VIDEO) g_hash_table_insert(gtkmedia->priv->remote_videos, key, widget); pidgin_media_get_widget(PidginMedia *gtkmedia, const gchar *session_id, const gchar *participant) GtkWidget *widget = NULL; gchar *key = create_key(session_id, participant); PurpleMediaSessionType type = purple_media_get_session_type(gtkmedia->priv->media, session_id); if (type & PURPLE_MEDIA_AUDIO) widget = g_hash_table_lookup(gtkmedia->priv->recv_progressbars, key); else if (type & PURPLE_MEDIA_VIDEO) widget = g_hash_table_lookup(gtkmedia->priv->remote_videos, key); pidgin_media_remove_widget(PidginMedia *gtkmedia, const gchar *session_id, const gchar *participant) GtkWidget *widget = pidgin_media_get_widget(gtkmedia, session_id, participant); PurpleMediaSessionType type = purple_media_get_session_type(gtkmedia->priv->media, session_id); gchar *key = create_key(session_id, participant); if (type & PURPLE_MEDIA_AUDIO) { g_hash_table_remove(gtkmedia->priv->recv_progressbars, key); if (g_hash_table_size(gtkmedia->priv->recv_progressbars) == 0 && gtkmedia->priv->send_progress) { gtk_widget_destroy(gtkmedia->priv->send_progress); gtkmedia->priv->send_progress = NULL; gtk_widget_destroy(gtkmedia->priv->mute); gtkmedia->priv->mute = NULL; } else if (type & PURPLE_MEDIA_VIDEO) { g_hash_table_remove(gtkmedia->priv->remote_videos, key); if (g_hash_table_size(gtkmedia->priv->remote_videos) == 0 && gtkmedia->priv->local_video) { gtk_widget_destroy(gtkmedia->priv->local_video); gtkmedia->priv->local_video = NULL; gtk_widget_destroy(gtkmedia->priv->pause); gtkmedia->priv->pause = NULL; gtk_widget_destroy(widget); gtk_widget_get_preferred_size(GTK_WIDGET(gtkmedia), NULL, &req); gtk_window_resize(GTK_WINDOW(gtkmedia), req.width, req.height); level_message_cb(PurpleMedia *media, gchar *session_id, gchar *participant, double level, PidginMedia *gtkmedia) GtkWidget *progress = NULL; progress = gtkmedia->priv->send_progress; progress = pidgin_media_get_widget(gtkmedia, session_id, participant); gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(progress), level); pidgin_media_disconnect_levels(PurpleMedia *media, PidginMedia *gtkmedia) PurpleMediaManager *manager = purple_media_get_manager(media); GstElement *element = purple_media_manager_get_pipeline(manager); gulong handler_id = g_signal_handler_find(G_OBJECT(gst_pipeline_get_bus(GST_PIPELINE(element))), G_SIGNAL_MATCH_FUNC | G_SIGNAL_MATCH_DATA, 0, 0, NULL, G_CALLBACK(level_message_cb), gtkmedia); g_signal_handler_disconnect(G_OBJECT(gst_pipeline_get_bus(GST_PIPELINE(element))), pidgin_media_dispose(GObject *media) PidginMedia *gtkmedia = PIDGIN_MEDIA(media); purple_debug_info("gtkmedia", "pidgin_media_dispose\n"); if (gtkmedia->priv->media) { purple_request_close_with_handle(gtkmedia); purple_media_remove_output_windows(gtkmedia->priv->media); pidgin_media_disconnect_levels(gtkmedia->priv->media, gtkmedia); g_object_unref(gtkmedia->priv->media); gtkmedia->priv->media = NULL; if (gtkmedia->priv->ui) { g_object_unref(gtkmedia->priv->ui); gtkmedia->priv->ui = NULL; if (gtkmedia->priv->timeout_id != 0) g_source_remove(gtkmedia->priv->timeout_id); if (gtkmedia->priv->recv_progressbars) { g_hash_table_destroy(gtkmedia->priv->recv_progressbars); g_hash_table_destroy(gtkmedia->priv->remote_videos); gtkmedia->priv->recv_progressbars = NULL; gtkmedia->priv->remote_videos = NULL; G_OBJECT_CLASS(parent_class)->dispose(media); pidgin_media_finalize(GObject *media) /* PidginMedia *gtkmedia = PIDGIN_MEDIA(media); */ purple_debug_info("gtkmedia", "pidgin_media_finalize\n"); G_OBJECT_CLASS(parent_class)->finalize(media); pidgin_media_emit_message(PidginMedia *gtkmedia, const char *msg) PurpleConversation *conv = purple_conversations_find_with_account( gtkmedia->priv->screenname, purple_media_get_account(gtkmedia->priv->media)); purple_conversation_write(conv, NULL, msg, PURPLE_MESSAGE_SYSTEM, time(NULL)); } PidginMediaRealizeData; realize_cb_cb(PidginMediaRealizeData *data) PidginMediaPrivate *priv = data->gtkmedia->priv; GdkWindow *window = NULL; if (data->participant == NULL) window = gtk_widget_get_window(priv->local_video); GtkWidget *widget = pidgin_media_get_widget(data->gtkmedia, data->session_id, data->participant); window = gtk_widget_get_window(widget); #ifdef GDK_WINDOWING_WIN32 if (GDK_IS_WIN32_WINDOW(window)) window_id = GPOINTER_TO_UINT(GDK_WINDOW_HWND(window)); if (GDK_IS_X11_WINDOW(window)) window_id = gdk_x11_window_get_xid(window); #ifdef GDK_WINDOWING_QUARTZ if (GDK_IS_QUARTZ_WINDOW(window)) window_id = (gulong)gdk_quartz_window_get_nsview(window); g_warning("Unsupported GDK backend"); #if !(defined(GDK_WINDOWING_WIN32) \ || defined(GDK_WINDOWING_X11) \ || defined(GDK_WINDOWING_QUARTZ)) # error "Unsupported GDK windowing system" purple_media_set_output_window(priv->media, data->session_id, data->participant, window_id); g_free(data->session_id); g_free(data->participant); realize_cb(GtkWidget *widget, PidginMediaRealizeData *data) g_timeout_add(0, (GSourceFunc)realize_cb_cb, data); pidgin_media_error_cb(PidginMedia *media, const char *error, PidginMedia *gtkmedia) PurpleConversation *conv = purple_conversations_find_with_account( gtkmedia->priv->screenname, purple_media_get_account(gtkmedia->priv->media)); purple_conversation_write(conv, NULL, error, PURPLE_MESSAGE_ERROR, time(NULL)); purple_notify_error(NULL, NULL, _("Media error"), error, purple_request_cpar_from_conversation(conv)); gtk_statusbar_push(GTK_STATUSBAR(gtkmedia->priv->statusbar), pidgin_media_accept_cb(PurpleMedia *media, int index) purple_media_stream_info(media, PURPLE_MEDIA_INFO_ACCEPT, pidgin_media_reject_cb(PurpleMedia *media, int index) GList *iter = purple_media_get_session_ids(media); for (; iter; iter = g_list_delete_link(iter, iter)) { const gchar *sessionid = iter->data; if (!purple_media_accepted(media, sessionid, NULL)) purple_media_stream_info(media, PURPLE_MEDIA_INFO_REJECT, pidgin_request_timeout_cb(PidginMedia *gtkmedia) PurpleMediaSessionType type; account = purple_media_get_account(gtkmedia->priv->media); buddy = purple_blist_find_buddy(account, gtkmedia->priv->screenname); alias = buddy ? purple_buddy_get_contact_alias(buddy) : gtkmedia->priv->screenname; type = gtkmedia->priv->request_type; gtkmedia->priv->timeout_id = 0; if (type & PURPLE_MEDIA_AUDIO && type & PURPLE_MEDIA_VIDEO) { message = g_strdup_printf(_("%s wishes to start an audio/video session with you."), } else if (type & PURPLE_MEDIA_AUDIO) { message = g_strdup_printf(_("%s wishes to start an audio session with you."), } else if (type & PURPLE_MEDIA_VIDEO) { message = g_strdup_printf(_("%s wishes to start a video session with you."), gtkmedia->priv->request_type = PURPLE_MEDIA_NONE; if (!purple_media_accepted(gtkmedia->priv->media, NULL, NULL)) { purple_request_accept_cancel(gtkmedia, _("Incoming Call"), message, NULL, PURPLE_DEFAULT_ACTION_NONE, purple_request_cpar_from_account(account), gtkmedia->priv->media, pidgin_media_accept_cb, pidgin_media_emit_message(gtkmedia, message); pidgin_media_input_volume_changed(GtkScaleButton *range, double value, double val = (double)value * 100.0; purple_media_set_input_volume(media, NULL, val); pidgin_media_output_volume_changed(GtkScaleButton *range, double value, double val = (double)value * 100.0; purple_media_set_output_volume(media, NULL, NULL, val); destroy_parent_widget_cb(GtkWidget *widget, GtkWidget *parent) g_return_if_fail(GTK_IS_WIDGET(parent)); gtk_widget_destroy(parent); pidgin_media_add_audio_widget(PidginMedia *gtkmedia, PurpleMediaSessionType type, const gchar *sid) GtkWidget *volume_widget, *progress_parent, *volume, *progress; if (type & PURPLE_MEDIA_SEND_AUDIO) { value = purple_prefs_get_int( "/purple/media/audio/volume/input"); } else if (type & PURPLE_MEDIA_RECV_AUDIO) { value = purple_prefs_get_int( "/purple/media/audio/volume/output"); g_return_val_if_reached(NULL); /* Setup widget structure */ volume_widget = gtk_hbox_new(FALSE, PIDGIN_HIG_BOX_SPACE); progress_parent = gtk_vbox_new(FALSE, 0); gtk_box_pack_start(GTK_BOX(volume_widget), progress_parent, TRUE, TRUE, 0); volume = gtk_volume_button_new(); gtk_scale_button_set_value(GTK_SCALE_BUTTON(volume), value/100.0); gtk_box_pack_end(GTK_BOX(volume_widget), volume, FALSE, FALSE, 0); /* Volume level indicator */ progress = gtk_progress_bar_new(); gtk_widget_set_size_request(progress, 250, 10); gtk_box_pack_end(GTK_BOX(progress_parent), progress, TRUE, FALSE, 0); if (type & PURPLE_MEDIA_SEND_AUDIO) { g_signal_connect (G_OBJECT(volume), "value-changed", G_CALLBACK(pidgin_media_input_volume_changed), gtkmedia->priv->send_progress = progress; } else if (type & PURPLE_MEDIA_RECV_AUDIO) { g_signal_connect (G_OBJECT(volume), "value-changed", G_CALLBACK(pidgin_media_output_volume_changed), pidgin_media_insert_widget(gtkmedia, progress, sid, gtkmedia->priv->screenname); g_signal_connect(G_OBJECT(progress), "destroy", G_CALLBACK(destroy_parent_widget_cb), gtk_widget_show_all(volume_widget); pidgin_media_ready_cb(PurpleMedia *media, PidginMedia *gtkmedia, const gchar *sid) GtkWidget *send_widget = NULL, *recv_widget = NULL, *button_widget = NULL; PurpleMediaSessionType type = purple_media_get_session_type(media, sid); if (gtkmedia->priv->recv_widget == NULL && type & (PURPLE_MEDIA_RECV_VIDEO | PURPLE_MEDIA_RECV_AUDIO)) { recv_widget = gtk_vbox_new(FALSE, PIDGIN_HIG_BOX_SPACE); gtk_box_pack_start(GTK_BOX(gtkmedia->priv->display), recv_widget, TRUE, TRUE, 0); gtk_widget_show(recv_widget); recv_widget = gtkmedia->priv->recv_widget; if (gtkmedia->priv->send_widget == NULL && type & (PURPLE_MEDIA_SEND_VIDEO | PURPLE_MEDIA_SEND_AUDIO)) { send_widget = gtk_vbox_new(FALSE, PIDGIN_HIG_BOX_SPACE); gtk_box_pack_start(GTK_BOX(gtkmedia->priv->display), send_widget, FALSE, TRUE, 0); button_widget = gtk_hbox_new(FALSE, PIDGIN_HIG_BOX_SPACE); gtk_box_pack_end(GTK_BOX(recv_widget), button_widget, gtk_widget_show(send_widget); gtk_toggle_button_new_with_mnemonic(_("_Hold")); gtk_box_pack_end(GTK_BOX(button_widget), gtkmedia->priv->hold, gtk_widget_show(gtkmedia->priv->hold); g_signal_connect(gtkmedia->priv->hold, "toggled", G_CALLBACK(pidgin_media_hold_toggled), send_widget = gtkmedia->priv->send_widget; button_widget = gtkmedia->priv->button_widget; if (type & PURPLE_MEDIA_RECV_VIDEO) { PidginMediaRealizeData *data; GdkColor color = {0, 0, 0, 0}; aspect = gtk_aspect_frame_new(NULL, 0, 0, 4.0/3.0, FALSE); gtk_frame_set_shadow_type(GTK_FRAME(aspect), GTK_SHADOW_IN); gtk_box_pack_start(GTK_BOX(recv_widget), aspect, TRUE, TRUE, 0); data = g_new0(PidginMediaRealizeData, 1); data->gtkmedia = gtkmedia; data->session_id = g_strdup(sid); data->participant = g_strdup(gtkmedia->priv->screenname); remote_video = gtk_drawing_area_new(); gtk_widget_modify_bg(remote_video, GTK_STATE_NORMAL, &color); g_signal_connect(G_OBJECT(remote_video), "realize", G_CALLBACK(realize_cb), data); gtk_container_add(GTK_CONTAINER(aspect), remote_video); gtk_widget_set_size_request (GTK_WIDGET(remote_video), 320, 240); g_signal_connect(G_OBJECT(remote_video), "destroy", G_CALLBACK(destroy_parent_widget_cb), aspect); gtk_widget_show(remote_video); pidgin_media_insert_widget(gtkmedia, remote_video, data->session_id, data->participant); if (type & PURPLE_MEDIA_SEND_VIDEO && !gtkmedia->priv->local_video) { PidginMediaRealizeData *data; GdkColor color = {0, 0, 0, 0}; aspect = gtk_aspect_frame_new(NULL, 0, 0, 4.0/3.0, TRUE); gtk_frame_set_shadow_type(GTK_FRAME(aspect), GTK_SHADOW_IN); gtk_box_pack_start(GTK_BOX(send_widget), aspect, FALSE, TRUE, 0); data = g_new0(PidginMediaRealizeData, 1); data->gtkmedia = gtkmedia; data->session_id = g_strdup(sid); data->participant = NULL; local_video = gtk_drawing_area_new(); gtk_widget_modify_bg(local_video, GTK_STATE_NORMAL, &color); g_signal_connect(G_OBJECT(local_video), "realize", G_CALLBACK(realize_cb), data); gtk_container_add(GTK_CONTAINER(aspect), local_video); gtk_widget_set_size_request (GTK_WIDGET(local_video), 80, 60); g_signal_connect(G_OBJECT(local_video), "destroy", G_CALLBACK(destroy_parent_widget_cb), aspect); gtk_widget_show(local_video); gtk_toggle_button_new_with_mnemonic(_("_Pause")); gtk_box_pack_end(GTK_BOX(button_widget), gtkmedia->priv->pause, gtk_widget_show(gtkmedia->priv->pause); g_signal_connect(gtkmedia->priv->pause, "toggled", G_CALLBACK(pidgin_media_pause_toggled), gtkmedia->priv->local_video = local_video; if (type & PURPLE_MEDIA_RECV_AUDIO) { gtk_box_pack_end(GTK_BOX(recv_widget), pidgin_media_add_audio_widget(gtkmedia, PURPLE_MEDIA_RECV_AUDIO, sid), FALSE, FALSE, 0); if (type & PURPLE_MEDIA_SEND_AUDIO) { gtk_toggle_button_new_with_mnemonic(_("_Mute")); gtk_box_pack_end(GTK_BOX(button_widget), gtkmedia->priv->mute, gtk_widget_show(gtkmedia->priv->mute); g_signal_connect(gtkmedia->priv->mute, "toggled", G_CALLBACK(pidgin_media_mute_toggled), gtk_box_pack_end(GTK_BOX(recv_widget), pidgin_media_add_audio_widget(gtkmedia, PURPLE_MEDIA_SEND_AUDIO, NULL), FALSE, FALSE, 0); if (type & PURPLE_MEDIA_AUDIO && gtkmedia->priv->level_handler_id == 0) { gtkmedia->priv->level_handler_id = g_signal_connect( media, "level", G_CALLBACK(level_message_cb), gtkmedia->priv->send_widget = send_widget; gtkmedia->priv->recv_widget = recv_widget; if (button_widget != NULL) { gtkmedia->priv->button_widget = button_widget; gtk_widget_show(GTK_WIDGET(button_widget)); if (purple_media_is_initiator(media, sid, NULL) == FALSE) { if (gtkmedia->priv->timeout_id != 0) g_source_remove(gtkmedia->priv->timeout_id); gtkmedia->priv->request_type |= type; gtkmedia->priv->timeout_id = g_timeout_add(500, (GSourceFunc)pidgin_request_timeout_cb, /* set the window icon according to the type */ if (type & PURPLE_MEDIA_VIDEO) { icon = gtk_widget_render_icon(GTK_WIDGET(gtkmedia), PIDGIN_STOCK_TOOLBAR_VIDEO_CALL, gtk_icon_size_from_name(PIDGIN_ICON_SIZE_TANGO_LARGE), NULL); } else if (type & PURPLE_MEDIA_AUDIO) { icon = gtk_widget_render_icon(GTK_WIDGET(gtkmedia), PIDGIN_STOCK_TOOLBAR_AUDIO_CALL, gtk_icon_size_from_name(PIDGIN_ICON_SIZE_TANGO_LARGE), NULL); gtk_window_set_icon(GTK_WINDOW(gtkmedia), icon); gtk_widget_show(gtkmedia->priv->display); pidgin_media_state_changed_cb(PurpleMedia *media, PurpleMediaState state, gchar *sid, gchar *name, PidginMedia *gtkmedia) purple_debug_info("gtkmedia", "state: %d sid: %s name: %s\n", state, sid ? sid : "(null)", name ? name : "(null)"); if (state == PURPLE_MEDIA_STATE_END) { if (sid != NULL && name != NULL) { pidgin_media_remove_widget(gtkmedia, sid, name); } else if (sid == NULL && name == NULL) { pidgin_media_emit_message(gtkmedia, _("The call has been terminated.")); gtk_widget_destroy(GTK_WIDGET(gtkmedia)); } else if (state == PURPLE_MEDIA_STATE_NEW && sid != NULL && name != NULL) { pidgin_media_ready_cb(media, gtkmedia, sid); pidgin_media_stream_info_cb(PurpleMedia *media, PurpleMediaInfoType type, gchar *sid, gchar *name, gboolean local, if (type == PURPLE_MEDIA_INFO_REJECT) { pidgin_media_emit_message(gtkmedia, _("You have rejected the call.")); } else if (type == PURPLE_MEDIA_INFO_ACCEPT) { purple_request_close_with_handle(gtkmedia); pidgin_media_set_state(gtkmedia, PIDGIN_MEDIA_ACCEPTED); pidgin_media_emit_message(gtkmedia, _("Call in progress.")); gtk_statusbar_push(GTK_STATUSBAR(gtkmedia->priv->statusbar), 0, _("Call in progress")); gtk_widget_show(GTK_WIDGET(gtkmedia)); pidgin_media_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) g_return_if_fail(PIDGIN_IS_MEDIA(object)); media = PIDGIN_MEDIA(object); g_object_unref(media->priv->media); media->priv->media = g_value_get_object(value); g_object_ref(media->priv->media); if (purple_media_is_initiator(media->priv->media, pidgin_media_set_state(media, PIDGIN_MEDIA_WAITING); pidgin_media_set_state(media, PIDGIN_MEDIA_REQUESTED); g_signal_connect(G_OBJECT(media->priv->media), "error", G_CALLBACK(pidgin_media_error_cb), media); g_signal_connect(G_OBJECT(media->priv->media), "state-changed", G_CALLBACK(pidgin_media_state_changed_cb), media); g_signal_connect(G_OBJECT(media->priv->media), "stream-info", G_CALLBACK(pidgin_media_stream_info_cb), media); if (media->priv->screenname) g_free(media->priv->screenname); media->priv->screenname = g_value_dup_string(value); G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); pidgin_media_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) g_return_if_fail(PIDGIN_IS_MEDIA(object)); media = PIDGIN_MEDIA(object); g_value_set_object(value, media->priv->media); g_value_set_string(value, media->priv->screenname); G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); pidgin_media_new(PurpleMedia *media, const gchar *screenname) PidginMedia *gtkmedia = g_object_new(pidgin_media_get_type(), "screenname", screenname, NULL); return GTK_WIDGET(gtkmedia); pidgin_media_set_state(PidginMedia *gtkmedia, PidginMediaState state) gtkmedia->priv->state = state; pidgin_media_new_cb(PurpleMediaManager *manager, PurpleMedia *media, PurpleAccount *account, gchar *screenname, gpointer nul) PidginMedia *gtkmedia = PIDGIN_MEDIA( pidgin_media_new(media, screenname)); PurpleBuddy *buddy = purple_blist_find_buddy(account, screenname); const gchar *alias = buddy ? purple_buddy_get_contact_alias(buddy) : screenname; gtk_window_set_title(GTK_WINDOW(gtkmedia), alias); if (purple_media_is_initiator(media, NULL, NULL) == TRUE) gtk_widget_show(GTK_WIDGET(gtkmedia)); create_vv_element(const gchar *plugin, const gchar *device) GstElement *element, *source; g_return_val_if_fail(plugin != NULL, NULL); g_return_val_if_fail(plugin[0] != '\0', NULL); if (device != NULL && device[0] == '\0') if (g_strcmp0(plugin, "videodisabledsrc") == 0) { element = gst_element_factory_make("videotestsrc", NULL); g_object_set(G_OBJECT(element), "is-live", TRUE, NULL); if (g_strcmp0(device, "snow") == 0) { /* GST_VIDEO_TEST_SRC_SNOW */ g_object_set(G_OBJECT(element), "pattern", 1, NULL); /* GST_VIDEO_TEST_SRC_BLACK */ g_object_set(G_OBJECT(element), "pattern", 2, NULL); if (g_strcmp0(plugin, "ksvideosrc") == 0) { GstElement *ksv_bin, *ksv_src, *ksv_filter; GstPad *ksv_pad, *ksv_ghost; ksv_bin = gst_bin_new("ksvideofilteredsrc"); ksv_src = gst_element_factory_make("ksvideosrc", NULL); ksv_filter = gst_element_factory_make("ffmpegcolorspace", NULL); gst_bin_add_many(GST_BIN(ksv_bin), ksv_src, ksv_filter, NULL); gst_element_link(ksv_src, ksv_filter); ksv_pad = gst_element_get_static_pad(ksv_filter, "src"); ksv_ghost = gst_ghost_pad_new("src", ksv_pad); gst_object_unref(ksv_pad); gst_element_add_pad(ksv_bin, ksv_ghost); element = source = gst_element_factory_make(plugin, NULL); GObjectClass *klass = G_OBJECT_GET_CLASS(source); if (g_object_class_find_property(klass, "device")) g_object_set(G_OBJECT(source), "device", device, NULL); else if (g_object_class_find_property(klass, "device-index")) g_object_set(G_OBJECT(source), "device-index", g_ascii_strtoull(device, NULL, 10), NULL); purple_debug_warning("gtkmedia", "No possibility to " if (g_strcmp0(plugin, "videotestsrc") == 0) g_object_set(G_OBJECT(element), "is-live", TRUE, NULL); create_configured_vv_element(const gchar *type, const gchar *dir) const gchar *plugin, *device; tmp = g_strdup_printf(PIDGIN_PREFS_ROOT "/vvconfig/%s/%s/plugin", plugin = purple_prefs_get_string(tmp); tmp = g_strdup_printf(PIDGIN_PREFS_ROOT "/vvconfig/%s/%s/device", device = purple_prefs_get_string(tmp); if (plugin == NULL || plugin[0] == '\0') return create_vv_element(plugin, device); create_default_video_src(PurpleMedia *media, const gchar *session_id, const gchar *participant) GstElement *sendbin, *src; src = create_configured_vv_element("video", "src"); src = create_vv_element("ksvideosrc", NULL); src = create_vv_element("dshowvideosrc", NULL); src = create_vv_element("autovideosrc", NULL); src = create_vv_element("osxvideosrc", NULL); src = create_vv_element("autovideosrc", NULL); src = create_vv_element("gconfvideosrc", NULL); src = create_vv_element("autovideosrc", NULL); src = create_vv_element("v4l2src", NULL); src = create_vv_element("v4lsrc", NULL); src = create_vv_element("videotestsrc", NULL); purple_debug_error("gtkmedia", "Unable to find a suitable " "element for the default video source.\n"); sendbin = gst_bin_new("pidgindefaultvideosrc"); gst_bin_add(GST_BIN(sendbin), src); pad = gst_element_get_static_pad(src, "src"); ghost = gst_ghost_pad_new("ghostsrc", pad); gst_element_add_pad(sendbin, ghost); create_default_video_sink(PurpleMedia *media, const gchar *session_id, const gchar *participant) sink = create_configured_vv_element("video", "sink"); sink = create_vv_element("directdrawsink", NULL); sink = create_vv_element("gconfvideosink", NULL); sink = create_vv_element("autovideosink", NULL); purple_debug_error("gtkmedia", "Unable to find a suitable " "element for the default video sink.\n"); create_default_audio_src(PurpleMedia *media, const gchar *session_id, const gchar *participant) src = create_configured_vv_element("audio", "src"); src = create_vv_element("directsoundsrc", NULL); src = create_vv_element("gconfaudiosrc", NULL); src = create_vv_element("autoaudiosrc", NULL); src = create_vv_element("alsasrc", NULL); src = create_vv_element("osssrc", NULL); src = create_vv_element("dshowaudiosrc", NULL); src = create_vv_element("osxaudiosrc", NULL); purple_debug_error("gtkmedia", "Unable to find a suitable " "element for the default audio source.\n"); gst_element_set_name(src, "pidgindefaultaudiosrc"); create_default_audio_sink(PurpleMedia *media, const gchar *session_id, const gchar *participant) sink = create_configured_vv_element("audio", "sink"); sink = create_vv_element("directsoundsink", NULL); sink = create_vv_element("gconfaudiosink", NULL); sink = create_vv_element("autoaudiosink",NULL); purple_debug_error("gtkmedia", "Unable to find a suitable " "element for the default audio sink.\n"); PurpleMediaManager *manager = purple_media_manager_get(); PurpleMediaElementInfo *default_video_src = g_object_new(PURPLE_TYPE_MEDIA_ELEMENT_INFO, "id", "pidgindefaultvideosrc", "name", "Pidgin Default Video Source", "type", PURPLE_MEDIA_ELEMENT_VIDEO | PURPLE_MEDIA_ELEMENT_SRC | PURPLE_MEDIA_ELEMENT_ONE_SRC | PURPLE_MEDIA_ELEMENT_UNIQUE, "create-cb", create_default_video_src, NULL); PurpleMediaElementInfo *default_video_sink = g_object_new(PURPLE_TYPE_MEDIA_ELEMENT_INFO, "id", "pidgindefaultvideosink", "name", "Pidgin Default Video Sink", "type", PURPLE_MEDIA_ELEMENT_VIDEO | PURPLE_MEDIA_ELEMENT_SINK | PURPLE_MEDIA_ELEMENT_ONE_SINK, "create-cb", create_default_video_sink, NULL); PurpleMediaElementInfo *default_audio_src = g_object_new(PURPLE_TYPE_MEDIA_ELEMENT_INFO, "id", "pidgindefaultaudiosrc", "name", "Pidgin Default Audio Source", "type", PURPLE_MEDIA_ELEMENT_AUDIO | PURPLE_MEDIA_ELEMENT_SRC | PURPLE_MEDIA_ELEMENT_ONE_SRC | PURPLE_MEDIA_ELEMENT_UNIQUE, "create-cb", create_default_audio_src, NULL); PurpleMediaElementInfo *default_audio_sink = g_object_new(PURPLE_TYPE_MEDIA_ELEMENT_INFO, "id", "pidgindefaultaudiosink", "name", "Pidgin Default Audio Sink", "type", PURPLE_MEDIA_ELEMENT_AUDIO | PURPLE_MEDIA_ELEMENT_SINK | PURPLE_MEDIA_ELEMENT_ONE_SINK, "create-cb", create_default_audio_sink, NULL); g_signal_connect(G_OBJECT(manager), "init-media", G_CALLBACK(pidgin_media_new_cb), NULL); purple_media_manager_set_ui_caps(manager, PURPLE_MEDIA_CAPS_AUDIO | PURPLE_MEDIA_CAPS_AUDIO_SINGLE_DIRECTION | PURPLE_MEDIA_CAPS_VIDEO | PURPLE_MEDIA_CAPS_VIDEO_SINGLE_DIRECTION | PURPLE_MEDIA_CAPS_AUDIO_VIDEO); purple_debug_info("gtkmedia", "Registering media element types\n"); purple_media_manager_set_active_element(manager, default_video_src); purple_media_manager_set_active_element(manager, default_video_sink); purple_media_manager_set_active_element(manager, default_audio_src); purple_media_manager_set_active_element(manager, default_audio_sink);