pidgin/pidgin
Clone
Summary
Browse
Changes
Graph
Clean up all of the fuzzers and add a corpus for all of them
release-2.x.y
2021-09-16, Gary Kramlich
4b6b116a586d
Clean up all of the fuzzers and add a corpus for all of them
Testing Done:
Ran all of the fuzzers, made sure they were able to add new corpra.
Reviewed at https://reviews.imfreedom.org/r/920/
/**
* @file media.c Account API
* @ingroup core
*
* Pidgin
*
* 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
* source distribution.
*
* 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
"internal.h"
#include
"debug.h"
#include
"connection.h"
#include
"media.h"
#include
"mediamanager.h"
#include
"pidgin.h"
#include
"request.h"
#include
"gtkmedia.h"
#include
"gtkutils.h"
#include
"pidginstock.h"
#ifdef USE_VV
#include
"media-gst.h"
#ifdef _WIN32
#include
<gdk/gdkwin32.h>
#endif
#include
<gdk/gdkkeysyms.h>
#if !GST_CHECK_VERSION(1,0,0)
#include
<gst/interfaces/xoverlay.h>
#endif
#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
;
typedef
enum
{
/* Waiting for response */
PIDGIN_MEDIA_WAITING
=
1
,
/* Got request */
PIDGIN_MEDIA_REQUESTED
,
/* Accepted call */
PIDGIN_MEDIA_ACCEPTED
,
/* Rejected call */
PIDGIN_MEDIA_REJECTED
,
}
PidginMediaState
;
struct
_PidginMediaClass
{
GtkWindowClass
parent_class
;
};
struct
_PidginMedia
{
GtkWindow
parent
;
PidginMediaPrivate
*
priv
;
};
struct
_PidginMediaPrivate
{
PurpleMedia
*
media
;
gchar
*
screenname
;
gulong
level_handler_id
;
GtkUIManager
*
ui
;
GtkWidget
*
menubar
;
GtkWidget
*
statusbar
;
GtkWidget
*
hold
;
GtkWidget
*
mute
;
GtkWidget
*
pause
;
GtkWidget
*
send_progress
;
GHashTable
*
recv_progressbars
;
PidginMediaState
state
;
GtkWidget
*
display
;
GtkWidget
*
send_widget
;
GtkWidget
*
recv_widget
;
GtkWidget
*
button_widget
;
GtkWidget
*
local_video
;
GHashTable
*
remote_videos
;
guint
timeout_id
;
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
;
#if 0
enum {
LAST_SIGNAL
};
static guint pidgin_media_signals[LAST_SIGNAL] = {0};
#endif
enum
{
PROP_0
,
PROP_MEDIA
,
PROP_SCREENNAME
};
static
GType
pidgin_media_get_type
(
void
)
{
static
GType
type
=
0
;
if
(
type
==
0
)
{
static
const
GTypeInfo
info
=
{
sizeof
(
PidginMediaClass
),
NULL
,
NULL
,
(
GClassInitFunc
)
pidgin_media_class_init
,
NULL
,
NULL
,
sizeof
(
PidginMedia
),
0
,
(
GInstanceInitFunc
)
pidgin_media_init
,
NULL
};
type
=
g_type_register_static
(
GTK_TYPE_WINDOW
,
"PidginMedia"
,
&
info
,
0
);
}
return
type
;
}
static
void
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"
,
"PurpleMedia"
,
"The PurpleMedia associated with this media."
,
PURPLE_TYPE_MEDIA
,
G_PARAM_CONSTRUCT_ONLY
|
G_PARAM_READWRITE
));
g_object_class_install_property
(
gobject_class
,
PROP_SCREENNAME
,
g_param_spec_string
(
"screenname"
,
"Screenname"
,
"The screenname of the user this session is with."
,
NULL
,
G_PARAM_CONSTRUCT_ONLY
|
G_PARAM_READWRITE
));
g_type_class_add_private
(
klass
,
sizeof
(
PidginMediaPrivate
));
}
static
void
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
,
NULL
,
NULL
,
TRUE
);
}
static
void
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
,
NULL
,
NULL
,
TRUE
);
}
static
void
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
,
NULL
,
NULL
,
TRUE
);
}
static
gboolean
pidgin_media_delete_event_cb
(
GtkWidget
*
widget
,
GdkEvent
*
event
,
PidginMedia
*
media
)
{
if
(
media
->
priv
->
media
)
purple_media_stream_info
(
media
->
priv
->
media
,
PURPLE_MEDIA_INFO_HANGUP
,
NULL
,
NULL
,
TRUE
);
return
FALSE
;
}
#ifdef HAVE_X11
static
int
pidgin_x_error_handler
(
Display
*
display
,
XErrorEvent
*
event
)
{
const
gchar
*
error_type
;
switch
(
event
->
error_code
)
{
#define XERRORCASE(type) case type: error_type = #type; break
XERRORCASE
(
BadAccess
);
XERRORCASE
(
BadAlloc
);
XERRORCASE
(
BadAtom
);
XERRORCASE
(
BadColor
);
XERRORCASE
(
BadCursor
);
XERRORCASE
(
BadDrawable
);
XERRORCASE
(
BadFont
);
XERRORCASE
(
BadGC
);
XERRORCASE
(
BadIDChoice
);
XERRORCASE
(
BadImplementation
);
XERRORCASE
(
BadLength
);
XERRORCASE
(
BadMatch
);
XERRORCASE
(
BadName
);
XERRORCASE
(
BadPixmap
);
XERRORCASE
(
BadRequest
);
XERRORCASE
(
BadValue
);
XERRORCASE
(
BadWindow
);
#undef XERRORCASE
default
:
error_type
=
"unknown"
;
break
;
}
purple_debug_error
(
"media"
,
"A %s Xlib error has occurred. "
"The program would normally crash now.
\n
"
,
error_type
);
return
0
;
}
#endif
static
void
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
=
"<ui>"
"<menubar name='Media'>"
"<menu action='MediaMenu'>"
"<menuitem action='Hangup'/>"
"</menu>"
"</menubar>"
"</ui>"
;
static
GtkWidget
*
setup_menubar
(
PidginMedia
*
window
)
{
GtkActionGroup
*
action_group
;
GError
*
error
;
GtkAccelGroup
*
accel_group
;
GtkWidget
*
menu
;
action_group
=
gtk_action_group_new
(
"MediaActions"
);
#ifdef ENABLE_NLS
gtk_action_group_set_translation_domain
(
action_group
,
PACKAGE
);
#endif
gtk_action_group_add_actions
(
action_group
,
menu_entries
,
G_N_ELEMENTS
(
menu_entries
),
GTK_WINDOW
(
window
));
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
);
error
=
NULL
;
if
(
!
gtk_ui_manager_add_ui_from_string
(
window
->
priv
->
ui
,
media_menu
,
-1
,
&
error
))
{
g_message
(
"building menus failed: %s"
,
error
->
message
);
g_error_free
(
error
);
exit
(
EXIT_FAILURE
);
}
menu
=
gtk_ui_manager_get_widget
(
window
->
priv
->
ui
,
"/Media"
);
gtk_widget_show
(
menu
);
return
menu
;
}
static
void
pidgin_media_init
(
PidginMedia
*
media
)
{
GtkWidget
*
vbox
;
media
->
priv
=
PIDGIN_MEDIA_GET_PRIVATE
(
media
);
#ifdef HAVE_X11
XSetErrorHandler
(
pidgin_x_error_handler
);
#endif
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
,
FALSE
,
FALSE
,
0
);
gtk_statusbar_push
(
GTK_STATUSBAR
(
media
->
priv
->
statusbar
),
0
,
_
(
"Calling..."
));
gtk_widget_show
(
media
->
priv
->
statusbar
);
media
->
priv
->
menubar
=
setup_menubar
(
media
);
gtk_box_pack_start
(
GTK_BOX
(
vbox
),
media
->
priv
->
menubar
,
FALSE
,
TRUE
,
0
);
media
->
priv
->
display
=
gtk_hbox_new
(
FALSE
,
PIDGIN_HIG_BOX_SPACE
);
gtk_container_set_border_width
(
GTK_CONTAINER
(
media
->
priv
->
display
),
PIDGIN_HIG_BOX_SPACE
);
gtk_box_pack_start
(
GTK_BOX
(
vbox
),
media
->
priv
->
display
,
TRUE
,
TRUE
,
PIDGIN_HIG_BOX_SPACE
);
gtk_widget_show
(
vbox
);
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
);
}
static
gchar
*
create_key
(
const
gchar
*
session_id
,
const
gchar
*
participant
)
{
return
g_strdup_printf
(
"%s_%s"
,
session_id
,
participant
);
}
static
void
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
);
}
static
GtkWidget
*
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
);
g_free
(
key
);
return
widget
;
}
static
void
pidgin_media_remove_widget
(
PidginMedia
*
gtkmedia
,
const
gchar
*
session_id
,
const
gchar
*
participant
)
{
GtkWidget
*
widget
=
pidgin_media_get_widget
(
gtkmedia
,
session_id
,
participant
);
if
(
widget
)
{
PurpleMediaSessionType
type
=
purple_media_get_session_type
(
gtkmedia
->
priv
->
media
,
session_id
);
gchar
*
key
=
create_key
(
session_id
,
participant
);
GtkRequisition
req
;
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
;
}
}
g_free
(
key
);
gtk_widget_destroy
(
widget
);
gtk_widget_size_request
(
GTK_WIDGET
(
gtkmedia
),
&
req
);
gtk_window_resize
(
GTK_WINDOW
(
gtkmedia
),
req
.
width
,
req
.
height
);
}
}
static
void
level_message_cb
(
PurpleMedia
*
media
,
gchar
*
session_id
,
gchar
*
participant
,
double
level
,
PidginMedia
*
gtkmedia
)
{
GtkWidget
*
progress
=
NULL
;
if
(
participant
==
NULL
)
progress
=
gtkmedia
->
priv
->
send_progress
;
else
progress
=
pidgin_media_get_widget
(
gtkmedia
,
session_id
,
participant
);
level
*=
5
;
if
(
level
>
1.0
)
level
=
1.0
;
if
(
progress
)
gtk_progress_bar_set_fraction
(
GTK_PROGRESS_BAR
(
progress
),
level
);
}
static
void
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
);
if
(
handler_id
)
g_signal_handler_disconnect
(
G_OBJECT
(
gst_pipeline_get_bus
(
GST_PIPELINE
(
element
))),
handler_id
);
}
static
void
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_signal_handlers_disconnect_matched
(
gtkmedia
->
priv
->
media
,
G_SIGNAL_MATCH_DATA
,
0
,
0
,
NULL
,
NULL
,
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
;
}
if
(
gtkmedia
->
priv
->
screenname
)
{
g_free
(
gtkmedia
->
priv
->
screenname
);
gtkmedia
->
priv
->
screenname
=
NULL
;
}
G_OBJECT_CLASS
(
parent_class
)
->
dispose
(
media
);
}
static
void
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
);
}
static
void
pidgin_media_emit_message
(
PidginMedia
*
gtkmedia
,
const
char
*
msg
)
{
PurpleConversation
*
conv
=
purple_find_conversation_with_account
(
PURPLE_CONV_TYPE_ANY
,
gtkmedia
->
priv
->
screenname
,
purple_media_get_account
(
gtkmedia
->
priv
->
media
));
if
(
conv
!=
NULL
)
purple_conversation_write
(
conv
,
NULL
,
msg
,
PURPLE_MESSAGE_SYSTEM
,
time
(
NULL
));
}
typedef
struct
{
PidginMedia
*
gtkmedia
;
gchar
*
session_id
;
gchar
*
participant
;
}
PidginMediaRealizeData
;
static
gboolean
realize_cb_cb
(
PidginMediaRealizeData
*
data
)
{
PidginMediaPrivate
*
priv
=
data
->
gtkmedia
->
priv
;
GdkWindow
*
window
=
NULL
;
if
(
priv
->
media
==
NULL
)
{
/* gtkmedia has been disposed */
goto
done
;
}
if
(
data
->
participant
==
NULL
)
#if GTK_CHECK_VERSION(2, 14, 0)
window
=
gtk_widget_get_window
(
priv
->
local_video
);
#else
window
=
(
priv
->
local_video
)
->
window
;
#endif
else
{
GtkWidget
*
widget
=
pidgin_media_get_widget
(
data
->
gtkmedia
,
data
->
session_id
,
data
->
participant
);
if
(
widget
)
#if GTK_CHECK_VERSION(2, 14, 0)
window
=
gtk_widget_get_window
(
widget
);
#else
window
=
widget
->
window
;
#endif
}
if
(
window
)
{
gulong
window_id
;
#ifdef _WIN32
window_id
=
GDK_WINDOW_HWND
(
window
);
#elif defined(HAVE_X11)
window_id
=
GDK_WINDOW_XWINDOW
(
window
);
#else
# error "Unsupported windowing system"
#endif
purple_media_set_output_window
(
priv
->
media
,
data
->
session_id
,
data
->
participant
,
window_id
);
}
done
:
g_free
(
data
->
session_id
);
g_free
(
data
->
participant
);
g_object_unref
(
data
->
gtkmedia
);
g_free
(
data
);
return
FALSE
;
}
static
void
realize_cb
(
GtkWidget
*
widget
,
PidginMediaRealizeData
*
data
)
{
g_timeout_add
(
0
,
(
GSourceFunc
)
realize_cb_cb
,
data
);
}
static
void
pidgin_media_error_cb
(
PidginMedia
*
media
,
const
char
*
error
,
PidginMedia
*
gtkmedia
)
{
PurpleConversation
*
conv
=
purple_find_conversation_with_account
(
PURPLE_CONV_TYPE_ANY
,
gtkmedia
->
priv
->
screenname
,
purple_media_get_account
(
gtkmedia
->
priv
->
media
));
if
(
conv
!=
NULL
)
purple_conversation_write
(
conv
,
NULL
,
error
,
PURPLE_MESSAGE_ERROR
,
time
(
NULL
));
else
purple_notify_error
(
NULL
,
NULL
,
_
(
"Media error"
),
error
);
gtk_statusbar_push
(
GTK_STATUSBAR
(
gtkmedia
->
priv
->
statusbar
),
0
,
error
);
}
static
void
pidgin_media_accept_cb
(
PurpleMedia
*
media
,
int
index
)
{
purple_media_stream_info
(
media
,
PURPLE_MEDIA_INFO_ACCEPT
,
NULL
,
NULL
,
TRUE
);
}
static
void
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
,
sessionid
,
NULL
,
TRUE
);
}
}
static
gboolean
pidgin_request_timeout_cb
(
PidginMedia
*
gtkmedia
)
{
PurpleAccount
*
account
;
PurpleBuddy
*
buddy
;
const
gchar
*
alias
;
PurpleMediaSessionType
type
;
gchar
*
message
=
NULL
;
account
=
purple_media_get_account
(
gtkmedia
->
priv
->
media
);
buddy
=
purple_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."
),
alias
);
}
else
if
(
type
&
PURPLE_MEDIA_AUDIO
)
{
message
=
g_strdup_printf
(
_
(
"%s wishes to start an audio session with you."
),
alias
);
}
else
if
(
type
&
PURPLE_MEDIA_VIDEO
)
{
message
=
g_strdup_printf
(
_
(
"%s wishes to start a video session with you."
),
alias
);
}
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
,
(
void
*
)
account
,
gtkmedia
->
priv
->
screenname
,
NULL
,
gtkmedia
->
priv
->
media
,
pidgin_media_accept_cb
,
pidgin_media_reject_cb
);
}
pidgin_media_emit_message
(
gtkmedia
,
message
);
g_free
(
message
);
return
FALSE
;
}
static
void
#if GTK_CHECK_VERSION(2,12,0)
pidgin_media_input_volume_changed
(
GtkScaleButton
*
range
,
double
value
,
PurpleMedia
*
media
)
{
double
val
=
(
double
)
value
*
100.0
;
#else
pidgin_media_input_volume_changed
(
GtkRange
*
range
,
PurpleMedia
*
media
)
{
double
val
=
(
double
)
gtk_range_get_value
(
GTK_RANGE
(
range
));
#endif
purple_media_set_input_volume
(
media
,
NULL
,
val
);
}
static
void
#if GTK_CHECK_VERSION(2,12,0)
pidgin_media_output_volume_changed
(
GtkScaleButton
*
range
,
double
value
,
PurpleMedia
*
media
)
{
double
val
=
(
double
)
value
*
100.0
;
#else
pidgin_media_output_volume_changed
(
GtkRange
*
range
,
PurpleMedia
*
media
)
{
double
val
=
(
double
)
gtk_range_get_value
(
GTK_RANGE
(
range
));
#endif
purple_media_set_output_volume
(
media
,
NULL
,
NULL
,
val
);
}
static
void
destroy_parent_widget_cb
(
GtkWidget
*
widget
,
GtkWidget
*
parent
)
{
g_return_if_fail
(
GTK_IS_WIDGET
(
parent
));
gtk_widget_destroy
(
parent
);
}
static
GtkWidget
*
pidgin_media_add_audio_widget
(
PidginMedia
*
gtkmedia
,
PurpleMediaSessionType
type
,
const
gchar
*
sid
)
{
GtkWidget
*
volume_widget
,
*
progress_parent
,
*
volume
,
*
progress
;
double
value
;
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"
);
}
else
g_return_val_if_reached
(
NULL
);
#if GTK_CHECK_VERSION(2,12,0)
/* 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 button */
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
);
#else
/* Setup widget structure */
volume_widget
=
gtk_vbox_new
(
FALSE
,
0
);
progress_parent
=
volume_widget
;
/* Volume slider */
volume
=
gtk_hscale_new_with_range
(
0.0
,
100.0
,
5.0
);
gtk_range_set_increments
(
GTK_RANGE
(
volume
),
5.0
,
25.0
);
gtk_range_set_value
(
GTK_RANGE
(
volume
),
value
);
gtk_scale_set_draw_value
(
GTK_SCALE
(
volume
),
FALSE
);
gtk_box_pack_end
(
GTK_BOX
(
volume_widget
),
volume
,
TRUE
,
FALSE
,
0
);
#endif
/* 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
->
media
);
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
),
gtkmedia
->
priv
->
media
);
pidgin_media_insert_widget
(
gtkmedia
,
progress
,
sid
,
gtkmedia
->
priv
->
screenname
);
}
g_signal_connect
(
G_OBJECT
(
progress
),
"destroy"
,
G_CALLBACK
(
destroy_parent_widget_cb
),
volume_widget
);
gtk_widget_show_all
(
volume_widget
);
return
volume_widget
;
}
static
void
phone_dtmf_pressed_cb
(
GtkButton
*
button
,
gpointer
user_data
)
{
PidginMedia
*
gtkmedia
=
user_data
;
gint
num
;
gchar
*
sid
;
num
=
GPOINTER_TO_INT
(
g_object_get_data
(
G_OBJECT
(
button
),
"dtmf-digit"
));
sid
=
g_object_get_data
(
G_OBJECT
(
button
),
"session-id"
);
purple_media_send_dtmf
(
gtkmedia
->
priv
->
media
,
sid
,
num
,
25
,
50
);
}
static
inline
GtkWidget
*
phone_create_button
(
const
gchar
*
text_hi
,
const
gchar
*
text_lo
)
{
GtkWidget
*
button
;
GtkWidget
*
label_hi
;
GtkWidget
*
label_lo
;
GtkWidget
*
grid
;
const
gchar
*
text_hi_local
;
if
(
text_hi
)
text_hi_local
=
_
(
text_hi
);
else
text_hi_local
=
""
;
grid
=
gtk_vbox_new
(
TRUE
,
0
);
button
=
gtk_button_new
();
label_hi
=
gtk_label_new
(
text_hi_local
);
gtk_misc_set_alignment
(
GTK_MISC
(
label_hi
),
0.5
,
0.5
);
gtk_box_pack_end
(
GTK_BOX
(
grid
),
label_hi
,
FALSE
,
TRUE
,
0
);
label_lo
=
gtk_label_new
(
text_lo
);
gtk_misc_set_alignment
(
GTK_MISC
(
label_lo
),
0.5
,
0.5
);
gtk_label_set_use_markup
(
GTK_LABEL
(
label_lo
),
TRUE
);
gtk_box_pack_end
(
GTK_BOX
(
grid
),
label_lo
,
FALSE
,
TRUE
,
0
);
gtk_container_add
(
GTK_CONTAINER
(
button
),
grid
);
return
button
;
}
static
struct
phone_label
{
gchar
*
subtext
;
gchar
*
text
;
gchar
chr
;
}
phone_labels
[]
=
{
{
"<b>1</b>"
,
NULL
,
'1'
},
/* Translators note: These are the letters on the keys of a numeric
keypad; translate according to the tables in §7 of ETSI ES 202 130:
http://webapp.etsi.org/WorkProgram/Report_WorkItem.asp?WKI_ID=11730
*/
/* Letters on the '2' key of a numeric keypad */
{
"<b>2</b>"
,
N_
(
"ABC"
),
'2'
},
/* Letters on the '3' key of a numeric keypad */
{
"<b>3</b>"
,
N_
(
"DEF"
),
'3'
},
/* Letters on the '4' key of a numeric keypad */
{
"<b>4</b>"
,
N_
(
"GHI"
),
'4'
},
/* Letters on the '5' key of a numeric keypad */
{
"<b>5</b>"
,
N_
(
"JKL"
),
'5'
},
/* Letters on the '6' key of a numeric keypad */
{
"<b>6</b>"
,
N_
(
"MNO"
),
'6'
},
/* Letters on the '7' key of a numeric keypad */
{
"<b>7</b>"
,
N_
(
"PQRS"
),
'7'
},
/* Letters on the '8' key of a numeric keypad */
{
"<b>8</b>"
,
N_
(
"TUV"
),
'8'
},
/* Letters on the '9' key of a numeric keypad */
{
"<b>9</b>"
,
N_
(
"WXYZ"
),
'9'
},
{
"<b>*</b>"
,
NULL
,
'*'
},
{
"<b>0</b>"
,
NULL
,
'0'
},
{
"<b>#</b>"
,
NULL
,
'#'
},
{
NULL
,
NULL
,
0
}
};
static
gboolean
pidgin_media_dtmf_key_press_event_cb
(
GtkWidget
*
widget
,
GdkEvent
*
event
,
gpointer
user_data
)
{
PidginMedia
*
gtkmedia
=
user_data
;
GdkEventKey
*
key
=
(
GdkEventKey
*
)
event
;
if
(
event
->
type
!=
GDK_KEY_PRESS
)
{
return
FALSE
;
}
if
((
key
->
keyval
>=
GDK_KEY_0
&&
key
->
keyval
<=
GDK_KEY_9
)
||
key
->
keyval
==
GDK_KEY_asterisk
||
key
->
keyval
==
GDK_KEY_numbersign
)
{
gchar
*
sid
=
g_object_get_data
(
G_OBJECT
(
widget
),
"session-id"
);
purple_media_send_dtmf
(
gtkmedia
->
priv
->
media
,
sid
,
key
->
keyval
,
25
,
50
);
}
return
FALSE
;
}
static
GtkWidget
*
pidgin_media_add_dtmf_widget
(
PidginMedia
*
gtkmedia
,
PurpleMediaSessionType
type
,
const
gchar
*
_sid
)
{
GtkWidget
*
grid
=
gtk_table_new
(
4
,
3
,
TRUE
);
GtkWidget
*
button
;
gint
index
=
0
;
GtkWindow
*
win
=
&
gtkmedia
->
parent
;
/* Add buttons */
for
(
index
=
0
;
phone_labels
[
index
].
subtext
!=
NULL
;
index
++
)
{
button
=
phone_create_button
(
phone_labels
[
index
].
text
,
phone_labels
[
index
].
subtext
);
g_signal_connect
(
button
,
"pressed"
,
G_CALLBACK
(
phone_dtmf_pressed_cb
),
gtkmedia
);
g_object_set_data
(
G_OBJECT
(
button
),
"dtmf-digit"
,
GINT_TO_POINTER
(
phone_labels
[
index
].
chr
));
g_object_set_data_full
(
G_OBJECT
(
button
),
"session-id"
,
g_strdup
(
_sid
),
g_free
);
gtk_table_attach
(
GTK_TABLE
(
grid
),
button
,
index
%
3
,
index
%
3
+
1
,
index
/
3
,
index
/
3
+
1
,
GTK_FILL
|
GTK_EXPAND
,
GTK_FILL
|
GTK_EXPAND
,
2
,
2
);
}
g_signal_connect
(
G_OBJECT
(
win
),
"key-press-event"
,
G_CALLBACK
(
pidgin_media_dtmf_key_press_event_cb
),
gtkmedia
);
g_object_set_data_full
(
G_OBJECT
(
win
),
"session-id"
,
g_strdup
(
_sid
),
g_free
);
gtk_widget_show_all
(
grid
);
return
grid
;
}
static
void
pidgin_media_ready_cb
(
PurpleMedia
*
media
,
PidginMedia
*
gtkmedia
,
const
gchar
*
sid
)
{
GtkWidget
*
send_widget
=
NULL
,
*
recv_widget
=
NULL
,
*
button_widget
=
NULL
;
PurpleMediaSessionType
type
=
purple_media_get_session_type
(
media
,
sid
);
GdkPixbuf
*
icon
=
NULL
;
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
);
}
else
{
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
,
FALSE
,
TRUE
,
0
);
gtk_widget_show
(
send_widget
);
/* Hold button */
gtkmedia
->
priv
->
hold
=
gtk_toggle_button_new_with_mnemonic
(
_
(
"_Hold"
));
gtk_box_pack_end
(
GTK_BOX
(
button_widget
),
gtkmedia
->
priv
->
hold
,
FALSE
,
FALSE
,
0
);
gtk_widget_show
(
gtkmedia
->
priv
->
hold
);
g_signal_connect
(
gtkmedia
->
priv
->
hold
,
"toggled"
,
G_CALLBACK
(
pidgin_media_hold_toggled
),
gtkmedia
);
}
else
{
send_widget
=
gtkmedia
->
priv
->
send_widget
;
button_widget
=
gtkmedia
->
priv
->
button_widget
;
}
if
(
type
&
PURPLE_MEDIA_RECV_VIDEO
)
{
PidginMediaRealizeData
*
data
;
GtkWidget
*
aspect
;
GtkWidget
*
remote_video
;
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
=
g_object_ref
(
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
);
gtk_widget_show
(
aspect
);
pidgin_media_insert_widget
(
gtkmedia
,
remote_video
,
data
->
session_id
,
data
->
participant
);
}
if
(
type
&
PURPLE_MEDIA_SEND_VIDEO
&&
!
gtkmedia
->
priv
->
local_video
)
{
PidginMediaRealizeData
*
data
;
GtkWidget
*
aspect
;
GtkWidget
*
local_video
;
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
=
g_object_ref
(
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_widget_show
(
aspect
);
gtkmedia
->
priv
->
pause
=
gtk_toggle_button_new_with_mnemonic
(
_
(
"_Pause"
));
gtk_box_pack_end
(
GTK_BOX
(
button_widget
),
gtkmedia
->
priv
->
pause
,
FALSE
,
FALSE
,
0
);
gtk_widget_show
(
gtkmedia
->
priv
->
pause
);
g_signal_connect
(
gtkmedia
->
priv
->
pause
,
"toggled"
,
G_CALLBACK
(
pidgin_media_pause_toggled
),
gtkmedia
);
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
)
{
gtkmedia
->
priv
->
mute
=
gtk_toggle_button_new_with_mnemonic
(
_
(
"_Mute"
));
gtk_box_pack_end
(
GTK_BOX
(
button_widget
),
gtkmedia
->
priv
->
mute
,
FALSE
,
FALSE
,
0
);
gtk_widget_show
(
gtkmedia
->
priv
->
mute
);
g_signal_connect
(
gtkmedia
->
priv
->
mute
,
"toggled"
,
G_CALLBACK
(
pidgin_media_mute_toggled
),
gtkmedia
);
gtk_box_pack_end
(
GTK_BOX
(
recv_widget
),
pidgin_media_add_audio_widget
(
gtkmedia
,
PURPLE_MEDIA_SEND_AUDIO
,
sid
),
FALSE
,
FALSE
,
0
);
gtk_box_pack_end
(
GTK_BOX
(
recv_widget
),
pidgin_media_add_dtmf_widget
(
gtkmedia
,
PURPLE_MEDIA_SEND_AUDIO
,
sid
),
FALSE
,
FALSE
,
0
);
}
if
(
type
&
PURPLE_MEDIA_AUDIO
&&
gtkmedia
->
priv
->
level_handler_id
==
0
)
{
gtkmedia
->
priv
->
level_handler_id
=
g_signal_connect
(
media
,
"level"
,
G_CALLBACK
(
level_message_cb
),
gtkmedia
);
}
if
(
send_widget
!=
NULL
)
gtkmedia
->
priv
->
send_widget
=
send_widget
;
if
(
recv_widget
!=
NULL
)
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
,
gtkmedia
);
}
/* 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
);
}
if
(
icon
)
{
gtk_window_set_icon
(
GTK_WINDOW
(
gtkmedia
),
icon
);
g_object_unref
(
icon
);
}
gtk_widget_show
(
gtkmedia
->
priv
->
display
);
}
static
void
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
);
}
}
static
void
pidgin_media_stream_info_cb
(
PurpleMedia
*
media
,
PurpleMediaInfoType
type
,
gchar
*
sid
,
gchar
*
name
,
gboolean
local
,
PidginMedia
*
gtkmedia
)
{
if
(
type
==
PURPLE_MEDIA_INFO_REJECT
)
{
pidgin_media_emit_message
(
gtkmedia
,
_
(
"You have rejected the call."
));
}
else
if
(
type
==
PURPLE_MEDIA_INFO_ACCEPT
)
{
if
(
local
==
TRUE
)
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
));
}
else
if
(
type
==
PURPLE_MEDIA_INFO_MUTE
&&
!
local
)
{
gtk_toggle_button_set_active
(
GTK_TOGGLE_BUTTON
(
gtkmedia
->
priv
->
mute
),
TRUE
);
}
else
if
(
type
==
PURPLE_MEDIA_INFO_UNMUTE
&&
!
local
)
{
gtk_toggle_button_set_active
(
GTK_TOGGLE_BUTTON
(
gtkmedia
->
priv
->
mute
),
FALSE
);
}
}
static
void
pidgin_media_video_caps_cb
(
PurpleMedia
*
media
,
gchar
*
session
,
gchar
*
participant
,
GstCaps
*
caps
,
PidginMedia
*
gtkmedia
)
{
GtkWidget
*
widget
;
GstStructure
*
str
;
gint
height
,
width
;
double
aspect
;
if
(
!
gst_caps_is_fixed
(
caps
))
return
;
widget
=
pidgin_media_get_widget
(
gtkmedia
,
session
,
participant
);
if
(
!
widget
)
return
;
widget
=
gtk_widget_get_parent
(
widget
);
if
(
!
widget
||
!
GTK_IS_ASPECT_FRAME
(
widget
))
return
;
str
=
gst_caps_get_structure
(
caps
,
0
);
if
(
!
str
)
return
;
if
(
!
gst_structure_get_int
(
str
,
"height"
,
&
height
)
||
!
gst_structure_get_int
(
str
,
"width"
,
&
width
)
||
height
<
1
||
width
<
1
)
return
;
aspect
=
(
double
)
width
/
(
double
)
height
;
gtk_aspect_frame_set
(
GTK_ASPECT_FRAME
(
widget
),
0
,
0
,
aspect
,
FALSE
);
}
static
void
pidgin_media_set_property
(
GObject
*
object
,
guint
prop_id
,
const
GValue
*
value
,
GParamSpec
*
pspec
)
{
PidginMedia
*
media
;
g_return_if_fail
(
PIDGIN_IS_MEDIA
(
object
));
media
=
PIDGIN_MEDIA
(
object
);
switch
(
prop_id
)
{
case
PROP_MEDIA
:
{
if
(
media
->
priv
->
media
)
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
,
NULL
,
NULL
)
==
TRUE
)
pidgin_media_set_state
(
media
,
PIDGIN_MEDIA_WAITING
);
else
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
);
g_signal_connect
(
G_OBJECT
(
media
->
priv
->
media
),
"video-caps"
,
G_CALLBACK
(
pidgin_media_video_caps_cb
),
media
);
break
;
}
case
PROP_SCREENNAME
:
if
(
media
->
priv
->
screenname
)
g_free
(
media
->
priv
->
screenname
);
media
->
priv
->
screenname
=
g_value_dup_string
(
value
);
break
;
default
:
G_OBJECT_WARN_INVALID_PROPERTY_ID
(
object
,
prop_id
,
pspec
);
break
;
}
}
static
void
pidgin_media_get_property
(
GObject
*
object
,
guint
prop_id
,
GValue
*
value
,
GParamSpec
*
pspec
)
{
PidginMedia
*
media
;
g_return_if_fail
(
PIDGIN_IS_MEDIA
(
object
));
media
=
PIDGIN_MEDIA
(
object
);
switch
(
prop_id
)
{
case
PROP_MEDIA
:
g_value_set_object
(
value
,
media
->
priv
->
media
);
break
;
case
PROP_SCREENNAME
:
g_value_set_string
(
value
,
media
->
priv
->
screenname
);
break
;
default
:
G_OBJECT_WARN_INVALID_PROPERTY_ID
(
object
,
prop_id
,
pspec
);
break
;
}
}
static
GtkWidget
*
pidgin_media_new
(
PurpleMedia
*
media
,
const
gchar
*
screenname
)
{
PidginMedia
*
gtkmedia
=
g_object_new
(
pidgin_media_get_type
(),
"media"
,
media
,
"screenname"
,
screenname
,
NULL
);
return
GTK_WIDGET
(
gtkmedia
);
}
static
void
pidgin_media_set_state
(
PidginMedia
*
gtkmedia
,
PidginMediaState
state
)
{
gtkmedia
->
priv
->
state
=
state
;
}
static
gboolean
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_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
));
return
TRUE
;
}
#if !defined(USE_GSTREAMER) || !GST_CHECK_VERSION(1,4,0)
static
void
videosink_disable_last_sample
(
GstElement
*
sink
)
{
GObjectClass
*
klass
=
G_OBJECT_GET_CLASS
(
sink
);
if
(
g_object_class_find_property
(
klass
,
"enable-last-sample"
))
{
g_object_set
(
sink
,
"enable-last-sample"
,
FALSE
,
NULL
);
}
}
static
void
autovideosink_child_added_cb
(
GstChildProxy
*
child_proxy
,
GObject
*
object
,
#if GST_CHECK_VERSION(1,0,0)
gchar
*
name
,
#endif
gpointer
user_data
)
{
videosink_disable_last_sample
(
GST_ELEMENT
(
object
));
}
static
GstElement
*
create_default_video_src
(
PurpleMedia
*
media
,
const
gchar
*
session_id
,
const
gchar
*
participant
)
{
GstElement
*
sendbin
,
*
src
;
GstPad
*
pad
;
GstPad
*
ghost
;
#ifdef _WIN32
/* autovideosrc doesn't pick ksvideosrc for some reason */
src
=
gst_element_factory_make
(
"ksvideosrc"
,
NULL
);
if
(
src
==
NULL
)
src
=
gst_element_factory_make
(
"dshowvideosrc"
,
NULL
);
if
(
src
==
NULL
)
src
=
gst_element_factory_make
(
"autovideosrc"
,
NULL
);
#else
src
=
gst_element_factory_make
(
"gconfvideosrc"
,
NULL
);
if
(
src
==
NULL
)
src
=
gst_element_factory_make
(
"autovideosrc"
,
NULL
);
if
(
src
==
NULL
)
src
=
gst_element_factory_make
(
"v4l2src"
,
NULL
);
if
(
src
==
NULL
)
src
=
gst_element_factory_make
(
"v4lsrc"
,
NULL
);
#endif
if
(
src
==
NULL
)
{
purple_debug_error
(
"gtkmedia"
,
"Unable to find a suitable "
"element for the default video source.
\n
"
);
return
NULL
;
}
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_object_unref
(
pad
);
gst_element_add_pad
(
sendbin
,
ghost
);
return
sendbin
;
}
static
GstElement
*
create_default_video_sink
(
PurpleMedia
*
media
,
const
gchar
*
session_id
,
const
gchar
*
participant
)
{
GstElement
*
sink
=
gst_element_factory_make
(
"gconfvideosink"
,
NULL
);
if
(
sink
==
NULL
)
sink
=
gst_element_factory_make
(
"autovideosink"
,
NULL
);
if
(
sink
==
NULL
)
purple_debug_error
(
"gtkmedia"
,
"Unable to find a suitable "
"element for the default video sink.
\n
"
);
if
(
sink
!=
NULL
)
g_signal_connect
(
sink
,
"child-added"
,
G_CALLBACK
(
autovideosink_child_added_cb
),
NULL
);
return
sink
;
}
static
GstElement
*
create_default_audio_src
(
PurpleMedia
*
media
,
const
gchar
*
session_id
,
const
gchar
*
participant
)
{
GstElement
*
src
;
src
=
gst_element_factory_make
(
"gconfaudiosrc"
,
NULL
);
if
(
src
==
NULL
)
src
=
gst_element_factory_make
(
"autoaudiosrc"
,
NULL
);
if
(
src
==
NULL
)
src
=
gst_element_factory_make
(
"alsasrc"
,
NULL
);
if
(
src
==
NULL
)
src
=
gst_element_factory_make
(
"osssrc"
,
NULL
);
if
(
src
==
NULL
)
src
=
gst_element_factory_make
(
"dshowaudiosrc"
,
NULL
);
if
(
src
==
NULL
)
{
purple_debug_error
(
"gtkmedia"
,
"Unable to find a suitable "
"element for the default audio source.
\n
"
);
return
NULL
;
}
gst_element_set_name
(
src
,
"pidgindefaultaudiosrc"
);
return
src
;
}
static
GstElement
*
create_default_audio_sink
(
PurpleMedia
*
media
,
const
gchar
*
session_id
,
const
gchar
*
participant
)
{
GstElement
*
sink
;
sink
=
gst_element_factory_make
(
"gconfaudiosink"
,
NULL
);
if
(
sink
==
NULL
)
sink
=
gst_element_factory_make
(
"autoaudiosink"
,
NULL
);
if
(
sink
==
NULL
)
{
purple_debug_error
(
"gtkmedia"
,
"Unable to find a suitable "
"element for the default audio sink.
\n
"
);
return
NULL
;
}
return
sink
;
}
#endif
/* USE_GSTREAMER >= 1.4 */
#endif
/* USE_VV */
void
pidgin_medias_init
(
void
)
{
#ifdef USE_VV
PurpleMediaManager
*
manager
=
purple_media_manager_get
();
PurpleMediaElementInfo
*
video_src
;
PurpleMediaElementInfo
*
video_sink
;
PurpleMediaElementInfo
*
audio_src
;
PurpleMediaElementInfo
*
audio_sink
;
#if defined(USE_GSTREAMER) && GST_CHECK_VERSION(1,4,0)
const
char
*
pref
;
pref
=
purple_prefs_get_string
(
PIDGIN_PREFS_ROOT
"/vvconfig/video/src/device"
);
video_src
=
purple_media_manager_get_element_info
(
manager
,
pref
);
if
(
!
video_src
)
{
pref
=
"autovideosrc"
;
purple_prefs_set_string
(
PIDGIN_PREFS_ROOT
"/vvconfig/video/src/device"
,
pref
);
video_src
=
purple_media_manager_get_element_info
(
manager
,
pref
);
}
pref
=
purple_prefs_get_string
(
PIDGIN_PREFS_ROOT
"/vvconfig/video/sink/device"
);
video_sink
=
purple_media_manager_get_element_info
(
manager
,
pref
);
if
(
!
video_sink
)
{
pref
=
"autovideosink"
;
purple_prefs_set_string
(
PIDGIN_PREFS_ROOT
"/vvconfig/video/sink/device"
,
pref
);
video_sink
=
purple_media_manager_get_element_info
(
manager
,
pref
);
}
pref
=
purple_prefs_get_string
(
PIDGIN_PREFS_ROOT
"/vvconfig/audio/src/device"
);
audio_src
=
purple_media_manager_get_element_info
(
manager
,
pref
);
if
(
!
audio_src
)
{
pref
=
"autoaudiosrc"
;
purple_prefs_set_string
(
PIDGIN_PREFS_ROOT
"/vvconfig/audio/src/device"
,
pref
);
audio_src
=
purple_media_manager_get_element_info
(
manager
,
pref
);
}
pref
=
purple_prefs_get_string
(
PIDGIN_PREFS_ROOT
"/vvconfig/audio/sink/device"
);
audio_sink
=
purple_media_manager_get_element_info
(
manager
,
pref
);
if
(
!
audio_sink
)
{
pref
=
"autoaudiosink"
;
purple_prefs_set_string
(
PIDGIN_PREFS_ROOT
"/vvconfig/audio/sink/device"
,
pref
);
audio_sink
=
purple_media_manager_get_element_info
(
manager
,
pref
);
}
#else
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
);
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
);
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
);
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
);
#endif
/* USE_GSTREAMER */
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
,
video_src
);
purple_media_manager_set_active_element
(
manager
,
video_sink
);
purple_media_manager_set_active_element
(
manager
,
audio_src
);
purple_media_manager_set_active_element
(
manager
,
audio_sink
);
#endif
}