aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--data/org.gnome.Empathy.gschema.xml.in5
-rw-r--r--libempathy/empathy-camera-monitor.c164
-rw-r--r--libempathy/empathy-camera-monitor.h13
-rw-r--r--libempathy/empathy-gsettings.h1
-rw-r--r--src/Makefile.am2
-rw-r--r--src/empathy-call-window.c87
-rw-r--r--src/empathy-call-window.h2
-rw-r--r--src/empathy-call-window.ui22
-rw-r--r--src/empathy-camera-menu.c387
-rw-r--r--src/empathy-camera-menu.h56
-rw-r--r--src/empathy-video-src.c28
-rw-r--r--src/empathy-video-src.h4
12 files changed, 737 insertions, 34 deletions
diff --git a/data/org.gnome.Empathy.gschema.xml.in b/data/org.gnome.Empathy.gschema.xml.in
index bf9f20fdd..3f887feed 100644
--- a/data/org.gnome.Empathy.gschema.xml.in
+++ b/data/org.gnome.Empathy.gschema.xml.in
@@ -231,6 +231,11 @@ present them to the user immediately.</_description>
<_summary>Call volume</_summary>
<_description>Call volume, as a percentage.</_description>
</key>
+ <key name="camera-device" type="s">
+ <default>''</default>
+ <_summary>Camera device</_summary>
+ <_description>Default camera device to use in video calls, e.g. /dev/video0.</_description>
+ </key>
</schema>
<schema id="org.gnome.Empathy.hints" path="/org/gnome/empathy/hints/">
<key name="close-main-window" type="b">
diff --git a/libempathy/empathy-camera-monitor.c b/libempathy/empathy-camera-monitor.c
index 3d80d40b7..3b5221eb8 100644
--- a/libempathy/empathy-camera-monitor.c
+++ b/libempathy/empathy-camera-monitor.c
@@ -34,6 +34,7 @@
struct _EmpathyCameraMonitorPrivate
{
CheeseCameraDeviceMonitor *cheese_monitor;
+ GQueue *cameras;
gint num_cameras;
};
@@ -43,10 +44,69 @@ enum
PROP_AVAILABLE,
};
+enum
+{
+ CAMERA_ADDED,
+ CAMERA_REMOVED,
+ LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL];
+
G_DEFINE_TYPE (EmpathyCameraMonitor, empathy_camera_monitor, G_TYPE_OBJECT);
static EmpathyCameraMonitor *manager_singleton = NULL;
+static EmpathyCamera *
+empathy_camera_new (const gchar *id,
+ const gchar *device,
+ const gchar *name)
+{
+ EmpathyCamera *camera = g_slice_new (EmpathyCamera);
+
+ camera->id = g_strdup (id);
+ camera->device = g_strdup (device);
+ camera->name = g_strdup (name);
+
+ return camera;
+}
+
+static EmpathyCamera *
+empathy_camera_copy (EmpathyCamera *camera)
+{
+ return empathy_camera_new (camera->id, camera->device, camera->name);
+}
+
+static void
+empathy_camera_free (EmpathyCamera *camera)
+{
+ g_free (camera->id);
+ g_free (camera->device);
+ g_free (camera->name);
+
+ g_slice_free (EmpathyCamera, camera);
+}
+
+G_DEFINE_BOXED_TYPE (EmpathyCamera, empathy_camera,
+ empathy_camera_copy, empathy_camera_free)
+
+static gint
+empathy_camera_find (gconstpointer a,
+ gconstpointer b)
+{
+ const EmpathyCamera *camera = a;
+ const gchar *id = b;
+
+ return g_strcmp0 (camera->id, id);
+}
+
+static void
+empathy_camera_monitor_free_camera_foreach (gpointer data,
+ gpointer user_data)
+{
+ empathy_camera_free (data);
+}
+
static void
on_camera_added (CheeseCameraDeviceMonitor *device,
gchar *id,
@@ -55,10 +115,19 @@ on_camera_added (CheeseCameraDeviceMonitor *device,
gint api_version,
EmpathyCameraMonitor *self)
{
+ EmpathyCamera *camera = empathy_camera_new (id, filename, product_name);
+
+ if (self->priv->cameras == NULL)
+ return;
+
+ g_queue_push_tail (self->priv->cameras, camera);
+
self->priv->num_cameras++;
if (self->priv->num_cameras == 1)
g_object_notify (G_OBJECT (self), "available");
+
+ g_signal_emit (self, signals[CAMERA_ADDED], 0, camera);
}
static void
@@ -66,10 +135,37 @@ on_camera_removed (CheeseCameraDeviceMonitor *device,
gchar *id,
EmpathyCameraMonitor *self)
{
+ EmpathyCamera *camera;
+ GList *l;
+
+ if (self->priv->cameras == NULL)
+ return;
+
+ l = g_queue_find_custom (self->priv->cameras, id, empathy_camera_find);
+
+ g_return_if_fail (l != NULL);
+
+ camera = l->data;
+
+ g_queue_delete_link (self->priv->cameras, l);
+
self->priv->num_cameras--;
if (self->priv->num_cameras == 0)
g_object_notify (G_OBJECT (self), "available");
+
+ g_signal_emit (self, signals[CAMERA_REMOVED], 0, camera);
+
+ empathy_camera_free (camera);
+}
+
+const GList *
+empathy_camera_monitor_get_cameras (EmpathyCameraMonitor *self)
+{
+ if (self->priv->cameras != NULL)
+ return self->priv->cameras->head;
+ else
+ return NULL;
}
static void
@@ -98,31 +194,11 @@ empathy_camera_monitor_dispose (GObject *object)
tp_clear_object (&self->priv->cheese_monitor);
- G_OBJECT_CLASS (empathy_camera_monitor_parent_class)->dispose (object);
-}
-
-static GObject *
-empathy_camera_monitor_constructor (GType type,
- guint n_props,
- GObjectConstructParam *props)
-{
- GObject *retval;
-
- if (manager_singleton)
- {
- retval = g_object_ref (manager_singleton);
- }
- else
- {
- retval =
- G_OBJECT_CLASS (empathy_camera_monitor_parent_class)->
- constructor (type, n_props, props);
+ g_queue_foreach (self->priv->cameras,
+ empathy_camera_monitor_free_camera_foreach, NULL);
+ tp_clear_pointer (&self->priv->cameras, g_queue_free);
- manager_singleton = EMPATHY_CAMERA_MONITOR (retval);
- g_object_add_weak_pointer (retval, (gpointer) & manager_singleton);
- }
-
- return retval;
+ G_OBJECT_CLASS (empathy_camera_monitor_parent_class)->dispose (object);
}
static void
@@ -141,7 +217,6 @@ empathy_camera_monitor_class_init (EmpathyCameraMonitorClass *klass)
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->dispose = empathy_camera_monitor_dispose;
- object_class->constructor = empathy_camera_monitor_constructor;
object_class->constructed = empathy_camera_monitor_constructed;
object_class->get_property = empathy_camera_monitor_get_property;
@@ -149,6 +224,20 @@ empathy_camera_monitor_class_init (EmpathyCameraMonitorClass *klass)
g_param_spec_boolean ("available", "Available",
"Camera available", TRUE, G_PARAM_READABLE));
+ signals[CAMERA_ADDED] =
+ g_signal_new ("added", G_OBJECT_CLASS_TYPE (klass),
+ G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+ 0, NULL, NULL,
+ g_cclosure_marshal_VOID__BOXED,
+ G_TYPE_NONE, 1, EMPATHY_TYPE_CAMERA);
+
+ signals[CAMERA_REMOVED] =
+ g_signal_new ("removed", G_OBJECT_CLASS_TYPE (klass),
+ G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+ 0, NULL, NULL,
+ g_cclosure_marshal_VOID__BOXED,
+ G_TYPE_NONE, 1, EMPATHY_TYPE_CAMERA);
+
g_type_class_add_private (object_class,
sizeof (EmpathyCameraMonitorPrivate));
}
@@ -159,6 +248,8 @@ empathy_camera_monitor_init (EmpathyCameraMonitor *self)
self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
EMPATHY_TYPE_CAMERA_MONITOR, EmpathyCameraMonitorPrivate);
+ self->priv->cameras = g_queue_new ();
+
self->priv->cheese_monitor = cheese_camera_device_monitor_new ();
g_signal_connect (self->priv->cheese_monitor, "added",
@@ -175,7 +266,28 @@ empathy_camera_monitor_init (EmpathyCameraMonitor *self)
EmpathyCameraMonitor *
empathy_camera_monitor_dup_singleton (void)
{
- return g_object_new (EMPATHY_TYPE_CAMERA_MONITOR, NULL);
+ GObject *retval;
+
+ if (manager_singleton)
+ {
+ retval = g_object_ref (manager_singleton);
+ }
+ else
+ {
+ retval = g_object_new (EMPATHY_TYPE_CAMERA_MONITOR, NULL);
+
+ manager_singleton = EMPATHY_CAMERA_MONITOR (retval);
+ g_object_add_weak_pointer (retval, (gpointer) &manager_singleton);
+ }
+
+ return EMPATHY_CAMERA_MONITOR (retval);
+}
+
+EmpathyCameraMonitor *
+empathy_camera_monitor_new (void)
+{
+ return EMPATHY_CAMERA_MONITOR (
+ g_object_new (EMPATHY_TYPE_CAMERA_MONITOR, NULL));
}
gboolean empathy_camera_monitor_get_available (EmpathyCameraMonitor *self)
diff --git a/libempathy/empathy-camera-monitor.h b/libempathy/empathy-camera-monitor.h
index d2d9bb89e..f627f7b58 100644
--- a/libempathy/empathy-camera-monitor.h
+++ b/libempathy/empathy-camera-monitor.h
@@ -46,11 +46,24 @@ struct _EmpathyCameraMonitorClass
GObjectClass parent_class;
};
+typedef struct
+{
+ gchar *id;
+ gchar *device;
+ gchar *name;
+} EmpathyCamera;
+
+#define EMPATHY_TYPE_CAMERA (empathy_camera_get_type ())
+GType empathy_camera_get_type (void) G_GNUC_CONST;
+
GType empathy_camera_monitor_get_type (void) G_GNUC_CONST;
EmpathyCameraMonitor *empathy_camera_monitor_dup_singleton (void);
+EmpathyCameraMonitor *empathy_camera_monitor_new (void);
gboolean empathy_camera_monitor_get_available (EmpathyCameraMonitor *self);
+const GList * empathy_camera_monitor_get_cameras (EmpathyCameraMonitor *self);
+
G_END_DECLS
#endif /* __EMPATHY_CAMERA_MONITOR_H__ */
diff --git a/libempathy/empathy-gsettings.h b/libempathy/empathy-gsettings.h
index 5a9fa95f8..d6e5d9192 100644
--- a/libempathy/empathy-gsettings.h
+++ b/libempathy/empathy-gsettings.h
@@ -53,6 +53,7 @@ G_BEGIN_DECLS
#define EMPATHY_PREFS_CALL_SCHEMA EMPATHY_PREFS_SCHEMA ".call"
#define EMPATHY_PREFS_CALL_SOUND_VOLUME "volume"
+#define EMPATHY_PREFS_CALL_CAMERA_DEVICE "camera-device"
#define EMPATHY_PREFS_CHAT_SCHEMA EMPATHY_PREFS_SCHEMA ".conversation"
#define EMPATHY_PREFS_CHAT_SHOW_SMILEYS "graphical-smileys"
diff --git a/src/Makefile.am b/src/Makefile.am
index fbbf989c0..cf5069fc6 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -173,6 +173,8 @@ empathy_call_SOURCES = \
empathy-preferences.h \
ev-sidebar.c \
ev-sidebar.h \
+ empathy-camera-menu.c \
+ empathy-camera-menu.h \
empathy-mic-menu.c \
empathy-mic-menu.h \
empathy-rounded-actor.c \
diff --git a/src/empathy-call-window.c b/src/empathy-call-window.c
index 361e0235f..b56613ca5 100644
--- a/src/empathy-call-window.c
+++ b/src/empathy-call-window.c
@@ -65,6 +65,7 @@
#include "empathy-mic-menu.h"
#include "empathy-preferences.h"
#include "empathy-rounded-actor.h"
+#include "empathy-camera-menu.h"
#define CONTENT_HBOX_BORDER_WIDTH 6
#define CONTENT_HBOX_SPACING 3
@@ -159,6 +160,7 @@ struct _EmpathyCallWindowPriv
ClutterActor *floating_toolbar;
GtkWidget *pane;
GtkAction *menu_fullscreen;
+ GtkAction *menu_swap_camera;
ClutterState *transitions;
@@ -245,6 +247,7 @@ struct _EmpathyCallWindowPriv
GSettings *settings;
EmpathyMicMenu *mic_menu;
+ EmpathyCameraMenu *camera_menu;
};
#define GET_PRIV(o) (EMPATHY_CALL_WINDOW (o)->priv)
@@ -514,6 +517,67 @@ empathy_call_window_maximise_camera_cb (GtkAction *action,
}
static void
+empathy_call_window_swap_camera_cb (GtkAction *action,
+ EmpathyCallWindow *self)
+{
+ const GList *cameras, *l;
+ gchar *current_cam;
+
+ DEBUG ("Swapping the camera");
+
+ cameras = empathy_camera_monitor_get_cameras (self->priv->camera_monitor);
+ current_cam = empathy_video_src_dup_device (
+ EMPATHY_GST_VIDEO_SRC (self->priv->video_input));
+
+ for (l = cameras; l != NULL; l = l->next)
+ {
+ EmpathyCamera *camera = l->data;
+
+ if (!tp_strdiff (camera->device, current_cam))
+ {
+ EmpathyCamera *next;
+
+ if (l->next != NULL)
+ next = l->next->data;
+ else
+ next = cameras->data;
+
+ /* EmpathyCameraMenu will update itself and do the actual change
+ * for us */
+ g_settings_set_string (self->priv->settings,
+ EMPATHY_PREFS_CALL_CAMERA_DEVICE,
+ next->device);
+
+ break;
+ }
+ }
+
+ g_free (current_cam);
+}
+
+static void
+empathy_call_window_camera_added_cb (EmpathyCameraMonitor *monitor,
+ EmpathyCamera *camera,
+ EmpathyCallWindow *self)
+{
+ const GList *cameras = empathy_camera_monitor_get_cameras (monitor);
+
+ gtk_action_set_visible (self->priv->menu_swap_camera,
+ g_list_length ((GList *) cameras) >= 2);
+}
+
+static void
+empathy_call_window_camera_removed_cb (EmpathyCameraMonitor *monitor,
+ EmpathyCamera *camera,
+ EmpathyCallWindow *self)
+{
+ const GList *cameras = empathy_camera_monitor_get_cameras (monitor);
+
+ gtk_action_set_visible (self->priv->menu_swap_camera,
+ g_list_length ((GList *) cameras) >= 2);
+}
+
+static void
empathy_call_window_preview_button_clicked_cb (GtkButton *button,
EmpathyCallWindow *self)
{
@@ -1254,6 +1318,7 @@ empathy_call_window_init (EmpathyCallWindow *self)
"bottom_toolbar", &priv->bottom_toolbar,
"ui_manager", &priv->ui_manager,
"menufullscreen", &priv->menu_fullscreen,
+ "menupreviewswap", &priv->menu_swap_camera,
"details_vbox", &priv->details_vbox,
"vcodec_encoding_label", &priv->vcodec_encoding_label,
"acodec_encoding_label", &priv->acodec_encoding_label,
@@ -1286,6 +1351,7 @@ empathy_call_window_init (EmpathyCallWindow *self)
"menupreviewdisable", "activate", empathy_call_window_disable_camera_cb,
"menupreviewminimise", "activate", empathy_call_window_minimise_camera_cb,
"menupreviewmaximise", "activate", empathy_call_window_maximise_camera_cb,
+ "menupreviewswap", "activate", empathy_call_window_swap_camera_cb,
NULL);
gtk_action_set_sensitive (priv->menu_fullscreen, FALSE);
@@ -1296,6 +1362,11 @@ empathy_call_window_init (EmpathyCallWindow *self)
priv->camera_button, "sensitive",
G_BINDING_SYNC_CREATE);
+ g_signal_connect (priv->camera_monitor, "added",
+ G_CALLBACK (empathy_call_window_camera_added_cb), self);
+ g_signal_connect (priv->camera_monitor, "removed",
+ G_CALLBACK (empathy_call_window_camera_removed_cb), self);
+
priv->lock = g_mutex_new ();
gtk_container_add (GTK_CONTAINER (self), top_vbox);
@@ -1460,6 +1531,7 @@ empathy_call_window_init (EmpathyCallWindow *self)
priv->sound_mgr = empathy_sound_manager_dup_singleton ();
priv->mic_menu = empathy_mic_menu_new (self);
+ priv->camera_menu = empathy_camera_menu_new (self);
empathy_call_window_show_hangup_button (self, TRUE);
@@ -1991,7 +2063,9 @@ empathy_call_window_dispose (GObject *object)
tp_clear_object (&priv->fullscreen);
tp_clear_object (&priv->camera_monitor);
tp_clear_object (&priv->settings);
- tp_clear_object (&priv->transitions);
+ tp_clear_object (&priv->sound_mgr);
+ tp_clear_object (&priv->mic_menu);
+ tp_clear_object (&priv->camera_menu);
g_list_free_full (priv->notifiers, g_object_unref);
@@ -2006,11 +2080,6 @@ empathy_call_window_dispose (GObject *object)
priv->contact = NULL;
}
-
- tp_clear_object (&priv->sound_mgr);
-
- tp_clear_object (&priv->mic_menu);
-
G_OBJECT_CLASS (empathy_call_window_parent_class)->dispose (object);
}
@@ -3536,3 +3605,9 @@ empathy_call_window_get_audio_src (EmpathyCallWindow *window)
return (EmpathyGstAudioSrc *) priv->audio_input;
}
+
+EmpathyGstVideoSrc *
+empathy_call_window_get_video_src (EmpathyCallWindow *self)
+{
+ return EMPATHY_GST_VIDEO_SRC (self->priv->video_input);
+}
diff --git a/src/empathy-call-window.h b/src/empathy-call-window.h
index 357d6f160..912a79173 100644
--- a/src/empathy-call-window.h
+++ b/src/empathy-call-window.h
@@ -26,6 +26,7 @@
#include "empathy-call-handler.h"
#include "empathy-audio-src.h"
+#include "empathy-video-src.h"
G_BEGIN_DECLS
@@ -66,6 +67,7 @@ EmpathyCallWindow *empathy_call_window_new (EmpathyCallHandler *handler);
GtkUIManager *empathy_call_window_get_ui_manager (EmpathyCallWindow *window);
EmpathyGstAudioSrc *empathy_call_window_get_audio_src (EmpathyCallWindow *window);
+EmpathyGstVideoSrc *empathy_call_window_get_video_src (EmpathyCallWindow *window);
G_END_DECLS
diff --git a/src/empathy-call-window.ui b/src/empathy-call-window.ui
index fbb1b92c6..6e89cb3c3 100644
--- a/src/empathy-call-window.ui
+++ b/src/empathy-call-window.ui
@@ -31,6 +31,13 @@
</object>
</child>
<child>
+ <object class="GtkAction" id="menucamera">
+ <property name="label" translatable="yes">_Camera</property>
+ <property name="name">menucamera</property>
+ <property name="icon_name">camera-web</property>
+ </object>
+ </child>
+ <child>
<object class="GtkAction" id="menusettings">
<property name="stock_id">gtk-preferences</property>
<property name="name">menusettings</property>
@@ -38,6 +45,12 @@
</object>
</child>
<child>
+ <object class="GtkAction" id="view">
+ <property name="name">view</property>
+ <property name="label" translatable="yes">_View</property>
+ </object>
+ </child>
+ <child>
<object class="GtkAction" id="help">
<property name="name">help</property>
<property name="label" translatable="yes">_Help</property>
@@ -64,6 +77,12 @@
</object>
</child>
<child>
+ <object class="GtkAction" id="menupreviewswap">
+ <property name="name">menupreviewswap</property>
+ <property name="label" translatable="yes">Swap camera</property>
+ </object>
+ </child>
+ <child>
<object class="GtkAction" id="menupreviewminimise">
<property name="name">menupreviewminimise</property>
<property name="label" translatable="yes">Minimise me</property>
@@ -90,6 +109,7 @@
</menu>
<menu action="edit">
<menu action="menumicrophone"/>
+ <menu action="menucamera"/>
<menuitem name="menusettings" action="menusettings"/>
</menu>
<menu action="help">
@@ -102,10 +122,12 @@
<menuitem name="menufullscreen" action="menufullscreen"/>
</popup>
<popup name="preview-menu">
+ <menuitem name="menupreviewswap" action="menupreviewswap"/>
<menuitem name="menupreviewminimise" action="menupreviewminimise"/>
<menuitem name="menupreviewdisable" action="menupreviewdisable"/>
</popup>
<popup name="preview-hidden-menu">
+ <menuitem name="menupreviewswap" action="menupreviewswap"/>
<menuitem name="menupreviewmaximise" action="menupreviewmaximise"/>
<menuitem name="menupreviewdisable" action="menupreviewdisable"/>
</popup>
diff --git a/src/empathy-camera-menu.c b/src/empathy-camera-menu.c
new file mode 100644
index 000000000..6a7d20c15
--- /dev/null
+++ b/src/empathy-camera-menu.c
@@ -0,0 +1,387 @@
+/*
+ * Copyright (C) 2011 Collabora Ltd.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * GtkAction code based on gnome-terminal's TerminalTabsMenu object.
+ * Thanks guys!
+ */
+
+#include <config.h>
+
+#include <gtk/gtk.h>
+
+#include <libempathy/empathy-camera-monitor.h>
+#include <libempathy/empathy-gsettings.h>
+
+#include "empathy-camera-menu.h"
+
+#define DEBUG_FLAG EMPATHY_DEBUG_VOIP
+#include <libempathy/empathy-debug.h>
+
+struct _EmpathyCameraMenuPrivate
+{
+ /* Borrowed ref; the call window actually owns us. */
+ EmpathyCallWindow *window;
+
+ /* Given away ref; the call window's UI manager now owns this. */
+ GtkActionGroup *action_group;
+
+ /* An invisible radio action so new cameras are always in the
+ * same radio group. */
+ GtkAction *anchor_action;
+
+ /* The merge ID used with the UI manager. We need to keep this
+ * around so in _clean we can remove all the items we've added
+ * before and start again. */
+ guint ui_id;
+
+ /* TRUE if we're in _update and so calling _set_active. */
+ gboolean in_update;
+
+ /* Queue of GtkRadioActions. */
+ GQueue *cameras;
+
+ EmpathyCameraMonitor *camera_monitor;
+
+ GSettings *settings;
+};
+
+G_DEFINE_TYPE (EmpathyCameraMenu, empathy_camera_menu, G_TYPE_OBJECT);
+
+enum
+{
+ PROP_WINDOW = 1,
+};
+
+static void empathy_camera_menu_update (EmpathyCameraMenu *self);
+
+static void
+empathy_camera_menu_init (EmpathyCameraMenu *self)
+{
+ self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
+ EMPATHY_TYPE_CAMERA_MENU, EmpathyCameraMenuPrivate);
+}
+
+static void
+empathy_camera_menu_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ EmpathyCameraMenu *self = EMPATHY_CAMERA_MENU (object);
+
+ switch (property_id)
+ {
+ case PROP_WINDOW:
+ self->priv->window = g_value_get_object (value);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ }
+}
+
+static void
+empathy_camera_menu_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ EmpathyCameraMenu *self = EMPATHY_CAMERA_MENU (object);
+
+ switch (property_id)
+ {
+ case PROP_WINDOW:
+ g_value_set_object (value, self->priv->window);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ }
+}
+
+static void
+empathy_camera_menu_clean (EmpathyCameraMenu *self)
+{
+ GtkUIManager *ui_manager;
+
+ if (self->priv->ui_id == 0)
+ return;
+
+ ui_manager = empathy_call_window_get_ui_manager (self->priv->window);
+
+ gtk_ui_manager_remove_ui (ui_manager, self->priv->ui_id);
+ gtk_ui_manager_ensure_update (ui_manager);
+ self->priv->ui_id = 0;
+}
+
+static void
+empathy_camera_menu_activate_cb (GtkAction *action,
+ EmpathyCameraMenu *self)
+{
+ EmpathyGstVideoSrc *video;
+ const gchar *device;
+
+ if (self->priv->in_update)
+ return;
+
+ video = empathy_call_window_get_video_src (self->priv->window);
+
+ device = gtk_action_get_name (action);
+
+ empathy_video_src_change_device (video, device);
+}
+
+static void
+empathy_camera_menu_update (EmpathyCameraMenu *self)
+{
+ GList *l;
+ GtkUIManager *ui_manager;
+ EmpathyGstVideoSrc *video;
+ gchar *current_camera;
+
+ ui_manager = empathy_call_window_get_ui_manager (self->priv->window);
+
+ video = empathy_call_window_get_video_src (self->priv->window);
+ current_camera = empathy_video_src_dup_device (video);
+
+ empathy_camera_menu_clean (self);
+ self->priv->ui_id = gtk_ui_manager_new_merge_id (ui_manager);
+
+ for (l = self->priv->cameras->head; l != NULL; l = g_list_next (l))
+ {
+ GtkRadioAction *action = l->data;
+ const gchar *name = gtk_action_get_name (GTK_ACTION (action));
+
+ if (!tp_strdiff (current_camera, name))
+ {
+ self->priv->in_update = TRUE;
+ gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action), TRUE);
+ self->priv->in_update = FALSE;
+ }
+
+ gtk_ui_manager_add_ui (ui_manager, self->priv->ui_id,
+ /* TODO: this should probably be passed from the call
+ * window, seeing that it's a reference to
+ * empathy-call-window.ui. */
+ "/menubar1/edit/menucamera",
+ name, name, GTK_UI_MANAGER_MENUITEM, FALSE);
+ }
+
+ g_free (current_camera);
+}
+
+static void
+empathy_camera_menu_add_camera (EmpathyCameraMenu *self,
+ EmpathyCamera *camera)
+{
+ GtkRadioAction *action;
+ GSList *group;
+
+ action = gtk_radio_action_new (camera->device, camera->name, NULL, NULL, 0);
+ gtk_action_group_add_action (self->priv->action_group, GTK_ACTION (action));
+
+ group = gtk_radio_action_get_group (
+ GTK_RADIO_ACTION (self->priv->anchor_action));
+ gtk_radio_action_set_group (GTK_RADIO_ACTION (action), group);
+
+ g_queue_push_tail (self->priv->cameras, action);
+
+ g_signal_connect (action, "activate",
+ G_CALLBACK (empathy_camera_menu_activate_cb), self);
+}
+
+static void
+empathy_camera_menu_camera_added_cb (EmpathyCameraMonitor *monitor,
+ EmpathyCamera *camera,
+ EmpathyCameraMenu *self)
+{
+ empathy_camera_menu_add_camera (self, camera);
+ empathy_camera_menu_update (self);
+}
+
+static void
+empathy_camera_menu_camera_removed_cb (EmpathyCameraMonitor *monitor,
+ EmpathyCamera *camera,
+ EmpathyCameraMenu *self)
+{
+ GList *l;
+
+ for (l = self->priv->cameras->head; l != NULL; l = g_list_next (l))
+ {
+ GtkAction *action = l->data;
+ const gchar *device;
+
+ device = gtk_action_get_name (action);
+
+ if (tp_strdiff (device, camera->device))
+ continue;
+
+ g_signal_handlers_disconnect_by_func (action,
+ G_CALLBACK (empathy_camera_menu_activate_cb), self);
+
+ gtk_action_group_remove_action (self->priv->action_group,
+ action);
+ g_queue_remove (self->priv->cameras, action);
+ break;
+ }
+
+ empathy_camera_menu_update (self);
+}
+
+static void
+empathy_camera_menu_prefs_camera_changed_cb (GSettings *settings,
+ gchar *key,
+ EmpathyCameraMenu *self)
+{
+ gchar *device = g_settings_get_string (settings, key);
+ GtkRadioAction *action = NULL;
+ gboolean found = FALSE;
+ GList *l;
+
+ for (l = self->priv->cameras->head; l != NULL; l = g_list_next (l))
+ {
+ const gchar *name;
+
+ action = l->data;
+ name = gtk_action_get_name (GTK_ACTION (action));
+
+ if (!tp_strdiff (device, name))
+ {
+ found = TRUE;
+ break;
+ }
+ }
+
+ /* If the selected camera isn't found, we connect the first
+ * available one */
+ if (!found && self->priv->cameras->head != NULL)
+ action = self->priv->cameras->head->data;
+
+ if (action != NULL &&
+ !gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)))
+ {
+ g_signal_handlers_block_by_func (settings,
+ empathy_camera_menu_prefs_camera_changed_cb, self);
+ gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action), TRUE);
+ g_signal_handlers_unblock_by_func (settings,
+ empathy_camera_menu_prefs_camera_changed_cb, self);
+ }
+
+ g_free (device);
+}
+
+static void
+empathy_camera_menu_get_cameras (EmpathyCameraMenu *self)
+{
+ const GList *cameras;
+
+ cameras = empathy_camera_monitor_get_cameras (self->priv->camera_monitor);
+
+ for (; cameras != NULL; cameras = g_list_next (cameras))
+ {
+ EmpathyCamera *camera = cameras->data;
+
+ empathy_camera_menu_add_camera (self, camera);
+ }
+
+ empathy_camera_menu_update (self);
+
+ /* Do as if the gsettings key had changed, so we select the key that
+ * was last set. */
+ empathy_camera_menu_prefs_camera_changed_cb (self->priv->settings,
+ EMPATHY_PREFS_CALL_CAMERA_DEVICE, self);
+}
+
+static void
+empathy_camera_menu_constructed (GObject *obj)
+{
+ EmpathyCameraMenu *self = EMPATHY_CAMERA_MENU (obj);
+ GtkUIManager *ui_manager;
+
+ g_assert (EMPATHY_IS_CALL_WINDOW (self->priv->window));
+
+ ui_manager = empathy_call_window_get_ui_manager (self->priv->window);
+
+ g_assert (GTK_IS_UI_MANAGER (ui_manager));
+
+ /* Okay let's go go go. */
+
+ self->priv->action_group = gtk_action_group_new ("EmpathyCameraMenu");
+ gtk_ui_manager_insert_action_group (ui_manager, self->priv->action_group, -1);
+ /* the UI manager now owns this */
+ g_object_unref (self->priv->action_group);
+
+ self->priv->anchor_action = g_object_new (GTK_TYPE_RADIO_ACTION,
+ "name", "EmpathyCameraMenuAnchorAction",
+ NULL);
+ gtk_action_group_add_action (self->priv->action_group,
+ self->priv->anchor_action);
+ g_object_unref (self->priv->anchor_action);
+
+ self->priv->camera_monitor = empathy_camera_monitor_new ();
+
+ tp_g_signal_connect_object (self->priv->camera_monitor, "added",
+ G_CALLBACK (empathy_camera_menu_camera_added_cb), self, 0);
+ tp_g_signal_connect_object (self->priv->camera_monitor, "removed",
+ G_CALLBACK (empathy_camera_menu_camera_removed_cb), self, 0);
+
+ self->priv->settings = g_settings_new (EMPATHY_PREFS_CALL_SCHEMA);
+ g_signal_connect (self->priv->settings,
+ "changed::"EMPATHY_PREFS_CALL_CAMERA_DEVICE,
+ G_CALLBACK (empathy_camera_menu_prefs_camera_changed_cb), self);
+
+ self->priv->cameras = g_queue_new ();
+
+ empathy_camera_menu_get_cameras (self);
+}
+
+static void
+empathy_camera_menu_dispose (GObject *obj)
+{
+ EmpathyCameraMenu *self = EMPATHY_CAMERA_MENU (obj);
+
+ tp_clear_pointer (&self->priv->cameras, g_queue_free);
+
+ tp_clear_object (&self->priv->camera_monitor);
+ tp_clear_object (&self->priv->settings);
+
+ G_OBJECT_CLASS (empathy_camera_menu_parent_class)->dispose (obj);
+}
+
+static void
+empathy_camera_menu_class_init (EmpathyCameraMenuClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->set_property = empathy_camera_menu_set_property;
+ object_class->get_property = empathy_camera_menu_get_property;
+ object_class->constructed = empathy_camera_menu_constructed;
+ object_class->dispose = empathy_camera_menu_dispose;
+
+ g_object_class_install_property (object_class, PROP_WINDOW,
+ g_param_spec_object ("window", "window", "window",
+ EMPATHY_TYPE_CALL_WINDOW,
+ G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT_ONLY));
+
+ g_type_class_add_private (object_class, sizeof (EmpathyCameraMenuPrivate));
+}
+
+EmpathyCameraMenu *
+empathy_camera_menu_new (EmpathyCallWindow *window)
+{
+ return g_object_new (EMPATHY_TYPE_CAMERA_MENU,
+ "window", window,
+ NULL);
+}
diff --git a/src/empathy-camera-menu.h b/src/empathy-camera-menu.h
new file mode 100644
index 000000000..f105baf22
--- /dev/null
+++ b/src/empathy-camera-menu.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2011 Collabora Ltd.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef __EMPATHY_CAMERA_MENU_H__
+#define __EMPATHY_CAMERA_MENU_H__
+
+#include <glib-object.h>
+
+#include "empathy-call-window.h"
+
+G_BEGIN_DECLS
+
+#define EMPATHY_TYPE_CAMERA_MENU (empathy_camera_menu_get_type ())
+#define EMPATHY_CAMERA_MENU(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), EMPATHY_TYPE_CAMERA_MENU, EmpathyCameraMenu))
+#define EMPATHY_CAMERA_MENU_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), EMPATHY_TYPE_CAMERA_MENU, EmpathyCameraMenuClass))
+#define EMPATHY_IS_CAMERA_MENU(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), EMPATHY_TYPE_CAMERA_MENU))
+#define EMPATHY_IS_CAMERA_MENU_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), EMPATHY_TYPE_CAMERA_MENU))
+#define EMPATHY_CAMERA_MENU_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), EMPATHY_TYPE_CAMERA_MENU, EmpathyCameraMenuClass))
+
+typedef struct _EmpathyCameraMenu EmpathyCameraMenu;
+typedef struct _EmpathyCameraMenuPrivate EmpathyCameraMenuPrivate;
+typedef struct _EmpathyCameraMenuClass EmpathyCameraMenuClass;
+
+struct _EmpathyCameraMenu
+{
+ GObject parent;
+ EmpathyCameraMenuPrivate *priv;
+};
+
+struct _EmpathyCameraMenuClass
+{
+ GObjectClass parent_class;
+};
+
+GType empathy_camera_menu_get_type (void) G_GNUC_CONST;
+
+EmpathyCameraMenu * empathy_camera_menu_new (EmpathyCallWindow *window);
+
+G_END_DECLS
+
+#endif /* __EMPATHY_CAMERA_MENU_H__ */
diff --git a/src/empathy-video-src.c b/src/empathy-video-src.c
index 15d8f79e1..ceb344215 100644
--- a/src/empathy-video-src.c
+++ b/src/empathy-video-src.c
@@ -123,8 +123,8 @@ empathy_video_src_init (EmpathyGstVideoSrc *obj)
/* allocate any data required by the object here */
if ((element = empathy_gst_add_to_bin (GST_BIN (obj),
- NULL, "gconfvideosrc")) == NULL)
- g_error ("Couldn't add \"gconfvideosrc\" (gst-plugins-good missing?)");
+ NULL, "v4l2src")) == NULL)
+ g_error ("Couldn't add \"v4l2src\" (gst-plugins-good missing?)");
/* we need to save our source to priv->src */
priv->src = element;
@@ -361,3 +361,27 @@ out:
return result;
}
+void
+empathy_video_src_change_device (EmpathyGstVideoSrc *self,
+ const gchar *device)
+{
+ EmpathyGstVideoSrcPrivate *priv = EMPATHY_GST_VIDEO_SRC_GET_PRIVATE (self);
+ GstState state;
+
+ gst_element_get_state (priv->src, &state, NULL, 0);
+
+ gst_element_set_state (priv->src, GST_STATE_NULL);
+ g_object_set (priv->src, "device", device, NULL);
+ gst_element_set_state (priv->src, state);
+}
+
+gchar *
+empathy_video_src_dup_device (EmpathyGstVideoSrc *self)
+{
+ EmpathyGstVideoSrcPrivate *priv = EMPATHY_GST_VIDEO_SRC_GET_PRIVATE (self);
+ gchar *device;
+
+ g_object_get (priv->src, "device", &device, NULL);
+
+ return device;
+}
diff --git a/src/empathy-video-src.h b/src/empathy-video-src.h
index fae5563eb..6a88b79eb 100644
--- a/src/empathy-video-src.h
+++ b/src/empathy-video-src.h
@@ -81,6 +81,10 @@ void empathy_video_src_set_channel (GstElement *src,
guint empathy_video_src_get_channel (GstElement *src,
EmpathyGstVideoSrcChannel channel);
+void empathy_video_src_change_device (EmpathyGstVideoSrc *self,
+ const gchar *device);
+gchar * empathy_video_src_dup_device (EmpathyGstVideoSrc *self);
+
G_END_DECLS
#endif /* #ifndef __EMPATHY_GST_VIDEO_SRC_H__*/