aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--configure.ac26
-rw-r--r--libempathy-gtk/empathy-individual-menu.c7
-rw-r--r--libempathy-gtk/empathy-log-window.c19
-rw-r--r--libempathy-gtk/empathy-new-call-dialog.c19
-rw-r--r--libempathy/Makefile.am19
-rw-r--r--libempathy/cheese-camera-device-monitor.c412
-rw-r--r--libempathy/cheese-camera-device-monitor.h67
-rw-r--r--libempathy/empathy-camera-monitor.c186
-rw-r--r--libempathy/empathy-camera-monitor.h56
-rw-r--r--libempathy/empathy-contact.c15
-rw-r--r--src/empathy-call-window.c24
11 files changed, 840 insertions, 10 deletions
diff --git a/configure.ac b/configure.ac
index c151b3a3f..e4cb024f4 100644
--- a/configure.ac
+++ b/configure.ac
@@ -371,6 +371,31 @@ fi
AM_CONDITIONAL(HAVE_WEBKIT, test "x$have_webkit" = "xyes")
# -----------------------------------------------------------
+# gudev
+# -----------------------------------------------------------
+AC_ARG_ENABLE(gudev,
+ AS_HELP_STRING([--enable-gudev=@<:@no/yes/auto@:>@],
+ [build with gudev support]), ,
+ enable_gudev=auto)
+
+if test "x$enable_gudev" != "xno"; then
+
+ PKG_CHECK_MODULES(UDEV, [gudev-1.0],
+ have_gudev="yes", have_gudev="no")
+
+ if test "x$have_gudev" = "xyes"; then
+ AC_DEFINE(HAVE_UDEV, 1, [Define if you have gudev])
+ fi
+else
+ have_gudev=no
+fi
+
+if test "x$enable_gudev" = "xyes" -a "x$have_gudev" != "xyes"; then
+ AC_MSG_ERROR([Could not find gudev dependencies.])
+fi
+AM_CONDITIONAL(HAVE_UDEV, test "x$have_gudev" = "xyes")
+
+# -----------------------------------------------------------
# spellchecking checks: enchant and iso-codes
# -----------------------------------------------------------
AC_ARG_ENABLE(spell,
@@ -604,6 +629,7 @@ Configure summary:
Adium themes (Webkit).......: ${have_webkit}
Meego widgets...............: ${have_meego}
Control center embedding....: ${have_control_center_embedding}
+ Camera monitoring...........: ${have_gudev}
Connectivity:
NetworkManager integration..: ${have_nm}
diff --git a/libempathy-gtk/empathy-individual-menu.c b/libempathy-gtk/empathy-individual-menu.c
index 1f01d8d54..5324c5013 100644
--- a/libempathy-gtk/empathy-individual-menu.c
+++ b/libempathy-gtk/empathy-individual-menu.c
@@ -31,6 +31,7 @@
#include <folks/folks.h>
#include <folks/folks-telepathy.h>
+#include <libempathy/empathy-camera-monitor.h>
#include <libempathy/empathy-dispatcher.h>
#include <libempathy/empathy-individual-manager.h>
#include <libempathy/empathy-chatroom-manager.h>
@@ -664,6 +665,7 @@ empathy_individual_video_call_menu_item_new (FolksIndividual *individual,
{
GtkWidget *item;
GtkWidget *image;
+ EmpathyCameraMonitor *monitor;
g_return_val_if_fail (FOLKS_IS_INDIVIDUAL (individual) ||
EMPATHY_IS_CONTACT (contact),
@@ -688,6 +690,11 @@ empathy_individual_video_call_menu_item_new (FolksIndividual *individual,
EMPATHY_ACTION_VIDEO_CALL);
}
+ monitor = empathy_camera_monitor_dup_singleton ();
+ g_object_set_data_full (G_OBJECT (item), "monitor", monitor, g_object_unref);
+ g_object_bind_property (monitor, "available", item, "sensitive",
+ G_BINDING_SYNC_CREATE);
+
return item;
}
diff --git a/libempathy-gtk/empathy-log-window.c b/libempathy-gtk/empathy-log-window.c
index 9f3ccfada..9b094652d 100644
--- a/libempathy-gtk/empathy-log-window.c
+++ b/libempathy-gtk/empathy-log-window.c
@@ -39,6 +39,7 @@
#include <extensions/extensions.h>
#include <libempathy/action-chain-internal.h>
+#include <libempathy/empathy-camera-monitor.h>
#include <libempathy/empathy-chatroom-manager.h>
#include <libempathy/empathy-chatroom.h>
#include <libempathy/empathy-message.h>
@@ -85,6 +86,9 @@ typedef struct
TplActionChain *chain;
TplLogManager *log_manager;
+ EmpathyCameraMonitor *camera_monitor;
+ GBinding *button_video_binding;
+
/* Used to cancel logger calls when no longer needed */
guint count;
@@ -447,6 +451,7 @@ empathy_log_window_show (TpAccount *account,
log_window->chain = _tpl_action_chain_new_async (NULL, NULL, NULL);
log_window->log_manager = tpl_log_manager_dup_singleton ();
+ log_window->camera_monitor = empathy_camera_monitor_dup_singleton ();
window = log_window;
@@ -577,6 +582,7 @@ log_window_destroy_cb (GtkWidget *widget,
g_free (window->last_find);
_tpl_action_chain_free (window->chain);
g_object_unref (window->log_manager);
+ tp_clear_object (&window->camera_monitor);
tp_clear_object (&window->selected_account);
g_free (window->selected_chat_id);
@@ -1513,6 +1519,8 @@ log_window_update_buttons_sensitivity (EmpathyLogWindow *window,
GtkTreePath *path;
gboolean profile, chat, call, video;
+ tp_clear_object (&window->button_video_binding);
+
profile = chat = call = video = FALSE;
if (!gtk_tree_model_get_iter_first (model, &iter))
@@ -1547,11 +1555,20 @@ log_window_update_buttons_sensitivity (EmpathyLogWindow *window,
call = capabilities & EMPATHY_CAPABILITIES_AUDIO;
video = capabilities & EMPATHY_CAPABILITIES_VIDEO;
+ if (video)
+ window->button_video_binding = g_object_bind_property (
+ window->camera_monitor, "available",
+ window->button_video, "sensitive",
+ G_BINDING_SYNC_CREATE);
+
out:
gtk_widget_set_sensitive (window->button_profile, profile);
gtk_widget_set_sensitive (window->button_chat, chat);
gtk_widget_set_sensitive (window->button_call, call);
- gtk_widget_set_sensitive (window->button_video, video);
+
+ /* Don't override the binding */
+ if (!video)
+ gtk_widget_set_sensitive (window->button_video, video);
}
static void
diff --git a/libempathy-gtk/empathy-new-call-dialog.c b/libempathy-gtk/empathy-new-call-dialog.c
index 0c609eefc..288802295 100644
--- a/libempathy-gtk/empathy-new-call-dialog.c
+++ b/libempathy-gtk/empathy-new-call-dialog.c
@@ -31,6 +31,7 @@
#include <telepathy-yell/telepathy-yell.h>
#include <libempathy/empathy-tp-contact-factory.h>
+#include <libempathy/empathy-camera-monitor.h>
#include <libempathy/empathy-contact-manager.h>
#include <libempathy/empathy-utils.h>
@@ -58,6 +59,8 @@ typedef struct {
struct _EmpathyNewCallDialogPriv {
GtkWidget *check_video;
+
+ EmpathyCameraMonitor *monitor;
};
#define GET_PRIV(o) \
@@ -177,6 +180,16 @@ empathy_new_call_dialog_account_filter (EmpathyContactSelectorDialog *dialog,
tp_proxy_prepare_async (connection, features, conn_prepared_cb, cb_data);
}
+static void
+empathy_new_call_dialog_dispose (GObject *object)
+{
+ EmpathyNewCallDialogPriv *priv = GET_PRIV (object);
+
+ tp_clear_object (&priv->monitor);
+
+ G_OBJECT_CLASS (empathy_new_call_dialog_parent_class)->dispose (object);
+}
+
static GObject *
empathy_new_call_dialog_constructor (GType type,
guint n_props,
@@ -210,8 +223,13 @@ empathy_new_call_dialog_init (EmpathyNewCallDialog *dialog)
EmpathyNewCallDialogPriv *priv = GET_PRIV (dialog);
GtkWidget *image;
+ priv->monitor = empathy_camera_monitor_dup_singleton ();
+
/* add video toggle */
priv->check_video = gtk_check_button_new_with_mnemonic (_("Send _Video"));
+ g_object_bind_property (priv->monitor, "available",
+ priv->check_video, "sensitive",
+ G_BINDING_SYNC_CREATE);
gtk_box_pack_end (GTK_BOX (parent->vbox), priv->check_video,
FALSE, TRUE, 0);
@@ -247,6 +265,7 @@ empathy_new_call_dialog_class_init (
g_type_class_add_private (class, sizeof (EmpathyNewCallDialogPriv));
object_class->constructor = empathy_new_call_dialog_constructor;
+ object_class->dispose = empathy_new_call_dialog_dispose;
dialog_class->response = empathy_new_call_dialog_response;
diff --git a/libempathy/Makefile.am b/libempathy/Makefile.am
index 571980dd8..56f2f3a79 100644
--- a/libempathy/Makefile.am
+++ b/libempathy/Makefile.am
@@ -13,6 +13,7 @@ AM_CPPFLAGS = \
$(GEOCLUE_CFLAGS) \
$(NETWORK_MANAGER_CFLAGS) \
$(CONNMAN_CFLAGS) \
+ $(UDEV_CFLAGS) \
$(WARN_CFLAGS) \
$(DISABLE_DEPRECATED)
@@ -29,7 +30,8 @@ libempathy_headers = \
action-chain-internal.h \
empathy-account-settings.h \
empathy-auth-factory.h \
- empathy-channel-factory.h \
+ empathy-camera-monitor.h \
+ empathy-channel-factory.h \
empathy-chatroom-manager.h \
empathy-chatroom.h \
empathy-connection-managers.h \
@@ -66,12 +68,13 @@ libempathy_headers = \
empathy-types.h \
empathy-utils.h
-libempathy_la_SOURCES = \
+libempathy_handwritten_source = \
$(libempathy_headers) \
action-chain.c \
empathy-account-settings.c \
empathy-auth-factory.c \
- empathy-channel-factory.c \
+ empathy-camera-monitor.c \
+ empathy-channel-factory.c \
empathy-chatroom-manager.c \
empathy-chatroom.c \
empathy-connection-managers.c \
@@ -105,6 +108,10 @@ libempathy_la_SOURCES = \
empathy-tp-streamed-media.c \
empathy-utils.c
+libempathy_la_SOURCES = \
+ $(libempathy_handwritten_source) \
+ cheese-camera-device-monitor.c cheese-camera-device-monitor.h
+
# do not distribute generated files
nodist_libempathy_la_SOURCES =\
$(BUILT_SOURCES)
@@ -115,11 +122,11 @@ libempathy_la_LIBADD = \
$(EMPATHY_LIBS) \
$(GEOCLUE_LIBS) \
$(NETWORK_MANAGER_LIBS) \
- $(CONNMAN_LIBS)
+ $(CONNMAN_LIBS) \
+ $(UDEV_LIBS)
check_c_sources = \
- $(libempathy_la_SOURCES) \
- $(libempathy_headers)
+ $(libempathy_handwritten_source)
include $(top_srcdir)/tools/check-coding-style.mk
check-local: check-coding-style
diff --git a/libempathy/cheese-camera-device-monitor.c b/libempathy/cheese-camera-device-monitor.c
new file mode 100644
index 000000000..85c6a0aa4
--- /dev/null
+++ b/libempathy/cheese-camera-device-monitor.c
@@ -0,0 +1,412 @@
+/*
+ * Copyright © 2007,2008 Jaap Haitsma <jaap@haitsma.org>
+ * Copyright © 2007-2009 daniel g. siegel <dgsiegel@gnome.org>
+ * Copyright © 2008 Ryan Zeigler <zeiglerr@gmail.com>
+ * Copyright © 2010 Filippo Argiolas <filippo.argiolas@gmail.com>
+ *
+ * Licensed under the GNU General Public License Version 2
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+#ifdef HAVE_CONFIG_H
+ #include <config.h>
+#endif
+
+#include <glib-object.h>
+#include <string.h>
+
+#ifdef HAVE_UDEV
+ #define G_UDEV_API_IS_SUBJECT_TO_CHANGE 1
+ #include <gudev/gudev.h>
+#else
+ #include <fcntl.h>
+ #include <unistd.h>
+ #include <sys/ioctl.h>
+ #if USE_SYS_VIDEOIO_H > 0
+ #include <sys/types.h>
+ #include <sys/videoio.h>
+ #elif defined (__sun)
+ #include <sys/types.h>
+ #include <sys/videodev2.h>
+ #endif /* USE_SYS_VIDEOIO_H */
+#endif
+
+#include "cheese-camera-device-monitor.h"
+#include "empathy-marshal.h"
+
+/**
+ * SECTION:cheese-camera-device-monitor
+ * @short_description: Simple object to enumerate v4l devices
+ * @include: cheese/cheese-camera-device-monitor.h
+ *
+ * #CheeseCameraDeviceMonitor provides a basic interface for
+ * video4linux device enumeration and hotplugging.
+ *
+ * It uses either GUdev or some platform specific code to list video
+ * devices. It is also capable (right now in linux only, with the
+ * udev backend) to monitor device plugging and emit a
+ * CheeseCameraDeviceMonitor::added or
+ * CheeseCameraDeviceMonitor::removed signal when an event happens.
+ */
+
+G_DEFINE_TYPE (CheeseCameraDeviceMonitor, cheese_camera_device_monitor, G_TYPE_OBJECT)
+
+#define CHEESE_CAMERA_DEVICE_MONITOR_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), \
+ CHEESE_TYPE_CAMERA_DEVICE_MONITOR, \
+ CheeseCameraDeviceMonitorPrivate))
+
+#define CHEESE_CAMERA_DEVICE_MONITOR_ERROR cheese_camera_device_monitor_error_quark ()
+
+GST_DEBUG_CATEGORY (cheese_device_monitor_cat);
+#define GST_CAT_DEFAULT cheese_device_monitor_cat
+
+enum CheeseCameraDeviceMonitorError
+{
+ CHEESE_CAMERA_DEVICE_MONITOR_ERROR_UNKNOWN,
+ CHEESE_CAMERA_DEVICE_MONITOR_ERROR_ELEMENT_NOT_FOUND
+};
+
+typedef struct
+{
+#ifdef HAVE_UDEV
+ GUdevClient *client;
+#else
+ guint filler;
+#endif /* HAVE_UDEV */
+} CheeseCameraDeviceMonitorPrivate;
+
+enum
+{
+ ADDED,
+ REMOVED,
+ LAST_SIGNAL
+};
+
+static guint monitor_signals[LAST_SIGNAL];
+
+#if 0
+GQuark
+cheese_camera_device_monitor_error_quark (void)
+{
+ return g_quark_from_static_string ("cheese-camera-error-quark");
+}
+#endif
+
+#ifdef HAVE_UDEV
+static void
+cheese_camera_device_monitor_added (CheeseCameraDeviceMonitor *monitor,
+ GUdevDevice *udevice)
+{
+ const char *device_file;
+ const char *product_name;
+ const char *vendor;
+ const char *product;
+ const char *bus;
+ gint vendor_id = 0;
+ gint product_id = 0;
+ gint v4l_version = 0;
+
+ const gchar *devpath = g_udev_device_get_property (udevice, "DEVPATH");
+
+ GST_INFO ("Checking udev device '%s'", devpath);
+
+ bus = g_udev_device_get_property (udevice, "ID_BUS");
+ if (g_strcmp0 (bus, "usb") == 0)
+ {
+ vendor = g_udev_device_get_property (udevice, "ID_VENDOR_ID");
+ if (vendor != NULL)
+ vendor_id = g_ascii_strtoll (vendor, NULL, 16);
+ product = g_udev_device_get_property (udevice, "ID_MODEL_ID");
+ if (product != NULL)
+ product_id = g_ascii_strtoll (product, NULL, 16);
+ if (vendor_id == 0 || product_id == 0)
+ {
+ GST_WARNING ("Error getting vendor and product id");
+ }
+ else
+ {
+ GST_INFO ("Found device %04x:%04x, getting capabilities...", vendor_id, product_id);
+ }
+ }
+ else
+ {
+ GST_INFO ("Not an usb device, skipping vendor and model id retrieval");
+ }
+
+ device_file = g_udev_device_get_device_file (udevice);
+ if (device_file == NULL)
+ {
+ GST_WARNING ("Error getting V4L device");
+ return;
+ }
+
+ /* vbi devices support capture capability too, but cannot be used,
+ * so detect them by device name */
+ if (strstr (device_file, "vbi"))
+ {
+ GST_INFO ("Skipping vbi device: %s", device_file);
+ return;
+ }
+
+ v4l_version = g_udev_device_get_property_as_int (udevice, "ID_V4L_VERSION");
+ if (v4l_version == 2 || v4l_version == 1)
+ {
+ const char *caps;
+
+ caps = g_udev_device_get_property (udevice, "ID_V4L_CAPABILITIES");
+ if (caps == NULL || strstr (caps, ":capture:") == NULL)
+ {
+ GST_WARNING ("Device %s seems to not have the capture capability, (radio tuner?)"
+ "Removing it from device list.", device_file);
+ return;
+ }
+ product_name = g_udev_device_get_property (udevice, "ID_V4L_PRODUCT");
+ }
+ else if (v4l_version == 0)
+ {
+ GST_ERROR ("Fix your udev installation to include v4l_id, ignoring %s", device_file);
+ return;
+ }
+ else
+ {
+ g_assert_not_reached ();
+ }
+
+ g_signal_emit (monitor, monitor_signals[ADDED], 0,
+ devpath,
+ device_file,
+ product_name,
+ v4l_version);
+}
+
+static void
+cheese_camera_device_monitor_removed (CheeseCameraDeviceMonitor *monitor,
+ GUdevDevice *udevice)
+{
+ g_signal_emit (monitor, monitor_signals[REMOVED], 0,
+ g_udev_device_get_property (udevice, "DEVPATH"));
+}
+
+static void
+cheese_camera_device_monitor_uevent_cb (GUdevClient *client,
+ const gchar *action,
+ GUdevDevice *udevice,
+ CheeseCameraDeviceMonitor *monitor)
+{
+ if (g_str_equal (action, "remove"))
+ cheese_camera_device_monitor_removed (monitor, udevice);
+ else if (g_str_equal (action, "add"))
+ cheese_camera_device_monitor_added (monitor, udevice);
+}
+
+/**
+ * cheese_camera_device_monitor_coldplug:
+ * @monitor: a #CheeseCameraDeviceMonitor object.
+ *
+ * Will actively look for plugged in cameras and emit
+ * ::added for those new cameras.
+ * This is only required when your program starts, so as to connect
+ * to those signals before they are emitted.
+ */
+void
+cheese_camera_device_monitor_coldplug (CheeseCameraDeviceMonitor *monitor)
+{
+ CheeseCameraDeviceMonitorPrivate *priv = CHEESE_CAMERA_DEVICE_MONITOR_GET_PRIVATE (monitor);
+ GList *devices, *l;
+ gint i = 0;
+
+ if (priv->client == NULL)
+ return;
+
+ GST_INFO ("Probing devices with udev...");
+
+ devices = g_udev_client_query_by_subsystem (priv->client, "video4linux");
+
+ /* Initialize camera structures */
+ for (l = devices; l != NULL; l = l->next)
+ {
+ cheese_camera_device_monitor_added (monitor, l->data);
+ g_object_unref (l->data);
+ i++;
+ }
+ g_list_free (devices);
+
+ if (i == 0) GST_WARNING ("No device found");
+}
+
+#else /* HAVE_UDEV */
+void
+cheese_camera_device_monitor_coldplug (CheeseCameraDeviceMonitor *monitor)
+{
+ #if 0
+ CheeseCameraDeviceMonitorPrivate *priv = CHEESE_CAMERA_DEVICE_MONITOR_GET_PRIVATE (monitor);
+ struct v4l2_capability v2cap;
+ struct video_capability v1cap;
+ int fd, ok;
+
+ if ((fd = open (device_path, O_RDONLY | O_NONBLOCK)) < 0)
+ {
+ g_warning ("Failed to open %s: %s", device_path, strerror (errno));
+ return;
+ }
+ ok = ioctl (fd, VIDIOC_QUERYCAP, &v2cap);
+ if (ok < 0)
+ {
+ ok = ioctl (fd, VIDIOCGCAP, &v1cap);
+ if (ok < 0)
+ {
+ g_warning ("Error while probing v4l capabilities for %s: %s",
+ device_path, strerror (errno));
+ close (fd);
+ return;
+ }
+ g_print ("Detected v4l device: %s\n", v1cap.name);
+ g_print ("Device type: %d\n", v1cap.type);
+ gstreamer_src = "v4lsrc";
+ product_name = v1cap.name;
+ }
+ else
+ {
+ guint cap = v2cap.capabilities;
+ g_print ("Detected v4l2 device: %s\n", v2cap.card);
+ g_print ("Driver: %s, version: %d\n", v2cap.driver, v2cap.version);
+
+ /* g_print ("Bus info: %s\n", v2cap.bus_info); */ /* Doesn't seem anything useful */
+ g_print ("Capabilities: 0x%08X\n", v2cap.capabilities);
+ if (!(cap & V4L2_CAP_VIDEO_CAPTURE))
+ {
+ g_print ("Device %s seems to not have the capture capability, (radio tuner?)\n"
+ "Removing it from device list.\n", device_path);
+ close (fd);
+ return;
+ }
+ gstreamer_src = "v4l2src";
+ product_name = (char *) v2cap.card;
+ }
+ close (fd);
+
+ GList *devices, *l;
+
+ g_print ("Probing devices with udev...\n");
+
+ if (priv->client == NULL)
+ return;
+
+ devices = g_udev_client_query_by_subsystem (priv->client, "video4linux");
+
+ /* Initialize camera structures */
+ for (l = devices; l != NULL; l = l->next)
+ {
+ cheese_camera_device_monitor_added (monitor, l->data);
+ g_object_unref (l->data);
+ }
+ g_list_free (devices);
+ #endif
+}
+
+#endif /* HAVE_UDEV */
+
+static void
+cheese_camera_device_monitor_finalize (GObject *object)
+{
+#ifdef HAVE_UDEV
+ CheeseCameraDeviceMonitor *monitor = CHEESE_CAMERA_DEVICE_MONITOR (object);
+ CheeseCameraDeviceMonitorPrivate *priv = CHEESE_CAMERA_DEVICE_MONITOR_GET_PRIVATE (monitor);
+
+ if (priv->client != NULL)
+ {
+ g_object_unref (priv->client);
+ priv->client = NULL;
+ }
+#endif /* HAVE_UDEV */
+ G_OBJECT_CLASS (cheese_camera_device_monitor_parent_class)->finalize (object);
+}
+
+static void
+cheese_camera_device_monitor_class_init (CheeseCameraDeviceMonitorClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ if (cheese_device_monitor_cat == NULL)
+ GST_DEBUG_CATEGORY_INIT (cheese_device_monitor_cat,
+ "cheese-device-monitor",
+ 0, "Cheese Camera Device Monitor");
+
+ object_class->finalize = cheese_camera_device_monitor_finalize;
+
+ /**
+ * CheeseCameraDeviceMonitor::added:
+ * @device: A private object representing the newly added camera.
+ * @id: Device unique identifier.
+ * @device: Device file name (e.g. /dev/video2).
+ * @product_name: Device product name (human readable, intended to be displayed in a UI).
+ * @api_version: Supported video4linux API: 1 for v4l, 2 for v4l2.
+ *
+ * The ::added signal is emitted when a camera is added, or on start-up
+ * after #cheese_camera_device_monitor_colplug is called.
+ **/
+ monitor_signals[ADDED] = g_signal_new ("added", G_OBJECT_CLASS_TYPE (klass),
+ G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+ G_STRUCT_OFFSET (CheeseCameraDeviceMonitorClass, added),
+ NULL, NULL,
+ _empathy_marshal_VOID__STRING_STRING_STRING_INT,
+ G_TYPE_NONE, 4, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_INT);
+
+ /**
+ * CheeseCameraDeviceMonitor::removed:
+ * @device: A private object representing the newly added camera
+ * @id: Device unique identifier.
+ *
+ * The ::removed signal is emitted when a camera is un-plugged, or
+ * disabled on the system.
+ **/
+ monitor_signals[REMOVED] = g_signal_new ("removed", G_OBJECT_CLASS_TYPE (klass),
+ G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+ G_STRUCT_OFFSET (CheeseCameraDeviceMonitorClass, removed),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__STRING,
+ G_TYPE_NONE, 1, G_TYPE_STRING);
+
+ g_type_class_add_private (klass, sizeof (CheeseCameraDeviceMonitorPrivate));
+}
+
+static void
+cheese_camera_device_monitor_init (CheeseCameraDeviceMonitor *monitor)
+{
+#ifdef HAVE_UDEV
+ CheeseCameraDeviceMonitorPrivate *priv = CHEESE_CAMERA_DEVICE_MONITOR_GET_PRIVATE (monitor);
+ const gchar *const subsystems[] = {"video4linux", NULL};
+
+ priv->client = g_udev_client_new (subsystems);
+ g_signal_connect (G_OBJECT (priv->client), "uevent",
+ G_CALLBACK (cheese_camera_device_monitor_uevent_cb), monitor);
+#endif /* HAVE_UDEV */
+}
+
+/**
+ * cheese_camera_device_monitor_new:
+ *
+ * Returns a new #CheeseCameraDeviceMonitor object.
+ *
+ * Return value: a new #CheeseCameraDeviceMonitor object.
+ **/
+CheeseCameraDeviceMonitor *
+cheese_camera_device_monitor_new (void)
+{
+ return g_object_new (CHEESE_TYPE_CAMERA_DEVICE_MONITOR, NULL);
+}
+
+/*
+ * vim: sw=2 ts=8 cindent noai bs=2
+ */
diff --git a/libempathy/cheese-camera-device-monitor.h b/libempathy/cheese-camera-device-monitor.h
new file mode 100644
index 000000000..d0d98a45f
--- /dev/null
+++ b/libempathy/cheese-camera-device-monitor.h
@@ -0,0 +1,67 @@
+/*
+ * Copyright © 2007,2008 Jaap Haitsma <jaap@haitsma.org>
+ * Copyright © 2007-2009 daniel g. siegel <dgsiegel@gnome.org>
+ * Copyright © 2008 Ryan zeigler <zeiglerr@gmail.com>
+ *
+ * Licensed under the GNU General Public License Version 2
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#ifndef __CHEESE_CAMERA_DEVICE_MONITOR_H__
+#define __CHEESE_CAMERA_DEVICE_MONITOR_H__
+
+#include <glib-object.h>
+#include <gst/interfaces/xoverlay.h>
+
+G_BEGIN_DECLS
+
+#define CHEESE_TYPE_CAMERA_DEVICE_MONITOR (cheese_camera_device_monitor_get_type ())
+#define CHEESE_CAMERA_DEVICE_MONITOR(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CHEESE_TYPE_CAMERA_DEVICE_MONITOR, \
+ CheeseCameraDeviceMonitor))
+#define CHEESE_CAMERA_DEVICE_MONITOR_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), CHEESE_TYPE_CAMERA_DEVICE_MONITOR, \
+ CheeseCameraDeviceMonitorClass))
+#define CHEESE_IS_CAMERA_DEVICE_MONITOR(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), CHEESE_TYPE_CAMERA_DEVICE_MONITOR))
+#define CHEESE_IS_CAMERA_DEVICE_MONITOR_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), CHEESE_TYPE_CAMERA_DEVICE_MONITOR))
+#define CHEESE_CAMERA_DEVICE_MONITOR_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), CHEESE_TYPE_CAMERA_DEVICE_MONITOR, \
+ CheeseCameraDeviceMonitorClass))
+
+typedef struct _CheeseCameraDeviceMonitorClass CheeseCameraDeviceMonitorClass;
+typedef struct _CheeseCameraDeviceMonitor CheeseCameraDeviceMonitor;
+
+struct _CheeseCameraDeviceMonitor
+{
+ GObject parent;
+};
+
+struct _CheeseCameraDeviceMonitorClass
+{
+ GObjectClass parent_class;
+
+ void (*added)(CheeseCameraDeviceMonitor *camera,
+ const char *id,
+ const char *device_file,
+ const char *product_name,
+ int api_version);
+ void (*removed)(CheeseCameraDeviceMonitor *camera, const char *id);
+};
+
+GType cheese_camera_device_monitor_get_type (void) G_GNUC_CONST;
+CheeseCameraDeviceMonitor *cheese_camera_device_monitor_new (void);
+void cheese_camera_device_monitor_coldplug (CheeseCameraDeviceMonitor *monitor);
+
+G_END_DECLS
+
+#endif /* __CHEESE_CAMERA_DEVICE_MONITOR_H__ */
diff --git a/libempathy/empathy-camera-monitor.c b/libempathy/empathy-camera-monitor.c
new file mode 100644
index 000000000..3d80d40b7
--- /dev/null
+++ b/libempathy/empathy-camera-monitor.c
@@ -0,0 +1,186 @@
+/*
+ * 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
+ *
+ * Authors: Emilio Pozuelo Monfort <emilio.pozuelo@collabora.co.uk>
+ */
+
+#include <config.h>
+
+#include <string.h>
+
+#include <telepathy-glib/util.h>
+
+#include "empathy-camera-monitor.h"
+#include "cheese-camera-device-monitor.h"
+#include "empathy-utils.h"
+
+#define DEBUG_FLAG EMPATHY_DEBUG_OTHER
+#include "empathy-debug.h"
+
+struct _EmpathyCameraMonitorPrivate
+{
+ CheeseCameraDeviceMonitor *cheese_monitor;
+ gint num_cameras;
+};
+
+enum
+{
+ PROP_0,
+ PROP_AVAILABLE,
+};
+
+G_DEFINE_TYPE (EmpathyCameraMonitor, empathy_camera_monitor, G_TYPE_OBJECT);
+
+static EmpathyCameraMonitor *manager_singleton = NULL;
+
+static void
+on_camera_added (CheeseCameraDeviceMonitor *device,
+ gchar *id,
+ gchar *filename,
+ gchar *product_name,
+ gint api_version,
+ EmpathyCameraMonitor *self)
+{
+ self->priv->num_cameras++;
+
+ if (self->priv->num_cameras == 1)
+ g_object_notify (G_OBJECT (self), "available");
+}
+
+static void
+on_camera_removed (CheeseCameraDeviceMonitor *device,
+ gchar *id,
+ EmpathyCameraMonitor *self)
+{
+ self->priv->num_cameras--;
+
+ if (self->priv->num_cameras == 0)
+ g_object_notify (G_OBJECT (self), "available");
+}
+
+static void
+empathy_camera_monitor_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ EmpathyCameraMonitor *self = (EmpathyCameraMonitor *) object;
+
+ switch (prop_id)
+ {
+ case PROP_AVAILABLE:
+ g_value_set_boolean (value, self->priv->num_cameras > 0);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+empathy_camera_monitor_dispose (GObject *object)
+{
+ EmpathyCameraMonitor *self = EMPATHY_CAMERA_MONITOR (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);
+
+ manager_singleton = EMPATHY_CAMERA_MONITOR (retval);
+ g_object_add_weak_pointer (retval, (gpointer) & manager_singleton);
+ }
+
+ return retval;
+}
+
+static void
+empathy_camera_monitor_constructed (GObject *object)
+{
+ EmpathyCameraMonitor *self = (EmpathyCameraMonitor *) object;
+
+ G_OBJECT_CLASS (empathy_camera_monitor_parent_class)->constructed (object);
+
+ cheese_camera_device_monitor_coldplug (self->priv->cheese_monitor);
+}
+
+static void
+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;
+
+ g_object_class_install_property (object_class, PROP_AVAILABLE,
+ g_param_spec_boolean ("available", "Available",
+ "Camera available", TRUE, G_PARAM_READABLE));
+
+ g_type_class_add_private (object_class,
+ sizeof (EmpathyCameraMonitorPrivate));
+}
+
+static void
+empathy_camera_monitor_init (EmpathyCameraMonitor *self)
+{
+ self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
+ EMPATHY_TYPE_CAMERA_MONITOR, EmpathyCameraMonitorPrivate);
+
+ self->priv->cheese_monitor = cheese_camera_device_monitor_new ();
+
+ g_signal_connect (self->priv->cheese_monitor, "added",
+ G_CALLBACK (on_camera_added), self);
+ g_signal_connect (self->priv->cheese_monitor, "removed",
+ G_CALLBACK (on_camera_removed), self);
+
+#ifndef HAVE_UDEV
+ /* No udev, assume there are cameras present */
+ self->priv->num_cameras = 1;
+#endif
+}
+
+EmpathyCameraMonitor *
+empathy_camera_monitor_dup_singleton (void)
+{
+ return g_object_new (EMPATHY_TYPE_CAMERA_MONITOR, NULL);
+}
+
+gboolean empathy_camera_monitor_get_available (EmpathyCameraMonitor *self)
+{
+ g_return_val_if_fail (EMPATHY_IS_CAMERA_MONITOR (self), FALSE);
+
+ return self->priv->num_cameras > 0;
+}
diff --git a/libempathy/empathy-camera-monitor.h b/libempathy/empathy-camera-monitor.h
new file mode 100644
index 000000000..d2d9bb89e
--- /dev/null
+++ b/libempathy/empathy-camera-monitor.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
+ *
+ * Authors: Emilio Pozuelo Monfort <emilio.pozuelo@collabora.co.uk>
+ */
+
+#ifndef __EMPATHY_CAMERA_MONITOR_H__
+#define __EMPATHY_CAMERA_MONITOR_H__
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+#define EMPATHY_TYPE_CAMERA_MONITOR (empathy_camera_monitor_get_type ())
+#define EMPATHY_CAMERA_MONITOR(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), EMPATHY_TYPE_CAMERA_MONITOR, EmpathyCameraMonitor))
+#define EMPATHY_CAMERA_MONITOR_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), EMPATHY_TYPE_CAMERA_MONITOR, EmpathyCameraMonitorClass))
+#define EMPATHY_IS_CAMERA_MONITOR(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), EMPATHY_TYPE_CAMERA_MONITOR))
+#define EMPATHY_IS_CAMERA_MONITOR_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), EMPATHY_TYPE_CAMERA_MONITOR))
+#define EMPATHY_CAMERA_MONITOR_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), EMPATHY_TYPE_CAMERA_MONITOR, EmpathyCameraMonitorClass))
+
+typedef struct _EmpathyCameraMonitor EmpathyCameraMonitor;
+typedef struct _EmpathyCameraMonitorClass EmpathyCameraMonitorClass;
+typedef struct _EmpathyCameraMonitorPrivate EmpathyCameraMonitorPrivate;
+
+struct _EmpathyCameraMonitor
+{
+ GObject parent;
+ EmpathyCameraMonitorPrivate *priv;
+};
+
+struct _EmpathyCameraMonitorClass
+{
+ GObjectClass parent_class;
+};
+
+GType empathy_camera_monitor_get_type (void) G_GNUC_CONST;
+
+EmpathyCameraMonitor *empathy_camera_monitor_dup_singleton (void);
+
+gboolean empathy_camera_monitor_get_available (EmpathyCameraMonitor *self);
+
+G_END_DECLS
+#endif /* __EMPATHY_CAMERA_MONITOR_H__ */
diff --git a/libempathy/empathy-contact.c b/libempathy/empathy-contact.c
index 42faad483..955ee885f 100644
--- a/libempathy/empathy-contact.c
+++ b/libempathy/empathy-contact.c
@@ -40,6 +40,7 @@
#endif
#include "empathy-contact.h"
+#include "empathy-camera-monitor.h"
#include "empathy-utils.h"
#include "empathy-enum-types.h"
#include "empathy-marshal.h"
@@ -1223,8 +1224,18 @@ empathy_contact_can_do_action (EmpathyContact *self,
sensitivity = empathy_contact_can_voip_audio (self);
break;
case EMPATHY_ACTION_VIDEO_CALL:
- sensitivity = empathy_contact_can_voip_video (self);
- break;
+ {
+ EmpathyCameraMonitor *monitor;
+
+ monitor = empathy_camera_monitor_dup_singleton ();
+
+ sensitivity = empathy_contact_can_voip_video (self);
+ sensitivity = sensitivity &&
+ empathy_camera_monitor_get_available (monitor);
+
+ g_object_unref (monitor);
+ break;
+ }
case EMPATHY_ACTION_VIEW_LOGS:
sensitivity = contact_has_log (self);
break;
diff --git a/src/empathy-call-window.c b/src/empathy-call-window.c
index 40258169c..4728e9f64 100644
--- a/src/empathy-call-window.c
+++ b/src/empathy-call-window.c
@@ -36,6 +36,7 @@
#include <gst/farsight/fs-element-added-notifier.h>
#include <gst/farsight/fs-utils.h>
+#include <libempathy/empathy-camera-monitor.h>
#include <libempathy/empathy-tp-contact-factory.h>
#include <libempathy/empathy-utils.h>
#include <libempathy-gtk/empathy-avatar-image.h>
@@ -101,6 +102,8 @@ struct _EmpathyCallWindowPriv
EmpathyContact *contact;
+ EmpathyCameraMonitor *camera_monitor;
+
guint call_state;
gboolean outgoing;
@@ -122,6 +125,7 @@ struct _EmpathyCallWindowPriv
GtkWidget *pane;
GtkAction *redial;
GtkAction *menu_fullscreen;
+ GtkAction *action_camera;
GtkAction *action_camera_on;
GtkWidget *tool_button_camera_off;
GtkWidget *tool_button_camera_preview;
@@ -976,6 +980,7 @@ empathy_call_window_init (EmpathyCallWindow *self)
"camera_off", &priv->tool_button_camera_off,
"camera_preview", &priv->tool_button_camera_preview,
"camera_on", &priv->tool_button_camera_on,
+ "camera", &priv->action_camera,
"action_camera_on", &priv->action_camera_on,
"details_vbox", &priv->details_vbox,
"vcodec_encoding_label", &priv->vcodec_encoding_label,
@@ -1008,6 +1013,21 @@ empathy_call_window_init (EmpathyCallWindow *self)
gtk_action_set_sensitive (priv->menu_fullscreen, FALSE);
+ priv->camera_monitor = empathy_camera_monitor_dup_singleton ();
+
+ g_object_bind_property (priv->camera_monitor, "available",
+ priv->tool_button_camera_off, "sensitive",
+ G_BINDING_SYNC_CREATE);
+ g_object_bind_property (priv->camera_monitor, "available",
+ priv->tool_button_camera_preview, "sensitive",
+ G_BINDING_SYNC_CREATE);
+ g_object_bind_property (priv->camera_monitor, "available",
+ priv->tool_button_camera_on, "sensitive",
+ G_BINDING_SYNC_CREATE);
+ g_object_bind_property (priv->camera_monitor, "available",
+ priv->action_camera, "sensitive",
+ G_BINDING_SYNC_CREATE);
+
priv->lock = g_mutex_new ();
gtk_container_add (GTK_CONTAINER (self), top_vbox);
@@ -1604,6 +1624,7 @@ empathy_call_window_dispose (GObject *object)
tp_clear_object (&priv->video_tee);
tp_clear_object (&priv->ui_manager);
tp_clear_object (&priv->fullscreen);
+ tp_clear_object (&priv->camera_monitor);
g_list_free_full (priv->notifiers, g_object_unref);
@@ -2380,7 +2401,8 @@ empathy_call_window_state_changed_cb (EmpathyCallHandler *handler,
empathy_sound_stop (EMPATHY_SOUND_PHONE_OUTGOING);
can_send_video = priv->video_input != NULL &&
- empathy_contact_can_voip_video (priv->contact);
+ empathy_contact_can_voip_video (priv->contact) &&
+ empathy_camera_monitor_get_available (priv->camera_monitor);
g_object_get (priv->handler, "call-channel", &call, NULL);