[gvfs] port gphoto2 backend and monitor to gudev
- From: David Zeuthen <davidz src gnome org>
- To: svn-commits-list gnome org
- Cc:
- Subject: [gvfs] port gphoto2 backend and monitor to gudev
- Date: Mon, 10 Aug 2009 01:10:54 +0000 (UTC)
commit 12c9c9ae561f9c572ec137d78c6da3bf9ba520bf
Author: Martin Pitt <martin pitt ubuntu com>
Date: Thu Jul 16 18:28:38 2009 +0200
port gphoto2 backend and monitor to gudev
This works with the ID_GPHOTO2 attribute, which was recently added to
libgphoto2:
http://sourceforge.net/tracker/?func=detail&aid=2801117&group_id=8874&atid=308874
That initial libgphoto2 patch does not yet pass the actual name, and just sets
ID_GPHOTO2="1". However, in this patch we check if it's not "1", and use that
as volume name (since it's the "best" one). Otherwise fall back to usb_id and
sysfs vendor/model/product names.
This also supports ID_MEDIA_PLAYER* attributes for media players with gphoto
support. Please see here for the discussion and development progress:
http://lists.freedesktop.org/archives/devkit-devel/2009-June/000226.html
http://cgit.freedesktop.org/~teuf/media-player-id/
http://bugzilla.gnome.org/show_bug.cgi?id=586410
Signed-off-by: David Zeuthen <davidz redhat com>
configure.ac | 11 ++-
daemon/Makefile.am | 11 ++-
daemon/gvfsbackendgphoto2.c | 150 ++++++++++++++++++++++--
monitor/gphoto2/Makefile.am | 25 ++++-
monitor/gphoto2/ggphoto2volume.c | 172 +++++++++++++++++++++++++++-
monitor/gphoto2/ggphoto2volume.h | 18 +++-
monitor/gphoto2/ggphoto2volumemonitor.c | 191 ++++++++++++++++++++++++++++++-
7 files changed, 558 insertions(+), 20 deletions(-)
---
diff --git a/configure.ac b/configure.ac
index 7f1ec16..446b98c 100644
--- a/configure.ac
+++ b/configure.ac
@@ -367,7 +367,7 @@ msg_gphoto2=no
GPHOTO2_LIBS=
GPHOTO2_CFLAGS=
-if test "x$enable_gphoto2" != "xno" -a "x$msg_hal" = "xyes" ; then
+if test "x$enable_gphoto2" != "xno" -a \( "x$msg_hal" = "xyes" -o "x$msg_gudev" = "xyes" \); then
PKG_CHECK_EXISTS(libgphoto2, msg_gphoto2=yes)
# Need OS tweaks in hal volume monitor backend
@@ -720,3 +720,12 @@ echo "
GNOME Keyring support: $msg_keyring
Bash-completion support: $msg_bash_completion
"
+
+# The gudev gphoto monitor needs a recent libgphoto; point to the required patch if the version is too old
+if test "x$msg_gudev" = "xyes"; then
+ PKG_CHECK_EXISTS(libgphoto2 >= 2.4.7,, msg_gphoto_patch=yes)
+ if test "x$msg_gphoto_patch" = "xyes"; then
+ AC_MSG_WARN([You are using a libgphoto2 version earlier than 2.4.7. To work with gudev, you must apply the patch in http://sourceforge.net/tracker/?func=detail&aid=2801117&group_id=8874&atid=308874])
+ fi
+fi
+
diff --git a/daemon/Makefile.am b/daemon/Makefile.am
index 733fa41..a03eba1 100644
--- a/daemon/Makefile.am
+++ b/daemon/Makefile.am
@@ -387,10 +387,19 @@ gvfsd_gphoto2_CPPFLAGS = \
-DBACKEND_HEADER=gvfsbackendgphoto2.h \
-DDEFAULT_BACKEND_TYPE=gphoto2 \
-DMAX_JOB_THREADS=1 \
- $(GPHOTO2_CFLAGS) $(HAL_CFLAGS) \
+ $(GPHOTO2_CFLAGS) \
-DBACKEND_TYPES='"gphoto2", G_VFS_TYPE_BACKEND_GPHOTO2,'
+if USE_GUDEV
+gvfsd_gphoto2_CPPFLAGS += $(GUDEV_CFLAGS)
+else
+gvfsd_gphoto2_CPPFLAGS += $(HAL_CFLAGS)
+endif
+if USE_GUDEV
+gvfsd_gphoto2_LDADD = $(libraries) $(GPHOTO2_LIBS) $(GUDEV_LIBS)
+else
gvfsd_gphoto2_LDADD = $(libraries) $(GPHOTO2_LIBS) $(HAL_LIBS)
+endif
gvfsd_http_SOURCES = \
soup-input-stream.c soup-input-stream.h \
diff --git a/daemon/gvfsbackendgphoto2.c b/daemon/gvfsbackendgphoto2.c
index 9ef15a6..c8177f3 100644
--- a/daemon/gvfsbackendgphoto2.c
+++ b/daemon/gvfsbackendgphoto2.c
@@ -35,8 +35,14 @@
#include <glib/gi18n.h>
#include <gio/gio.h>
#include <gphoto2.h>
-#include <libhal.h>
-#include <dbus/dbus.h>
+#ifdef HAVE_GUDEV
+ #include <gudev/gudev.h>
+#elif defined(HAVE_HAL)
+ #include <libhal.h>
+ #include <dbus/dbus.h>
+#else
+ #error Needs hal or gudev
+#endif
#include <sys/time.h>
#include "gvfsbackendgphoto2.h"
@@ -176,11 +182,16 @@ struct _GVfsBackendGphoto2
/* see comment in ensure_ignore_prefix() */
char *ignore_prefix;
+#ifdef HAVE_GUDEV
+ GUdevClient *gudev_client;
+ GUdevDevice *udev_device;
+#elif defined(HAVE_HAL)
DBusConnection *dbus_connection;
LibHalContext *hal_ctx;
char *hal_udi;
char *hal_name;
- char *hal_icon_name;
+#endif
+ char *icon_name;
/* whether we can write to the device */
gboolean can_write;
@@ -542,6 +553,13 @@ release_device (GVfsBackendGphoto2 *gphoto2_backend)
gphoto2_backend->camera = NULL;
}
+#ifdef HAVE_GUDEV
+ if (gphoto2_backend->gudev_client != NULL)
+ g_object_unref (gphoto2_backend->gudev_client);
+ if (gphoto2_backend->udev_device != NULL)
+ g_object_unref (gphoto2_backend->udev_device);
+
+#elif defined(HAVE_HAL)
if (gphoto2_backend->dbus_connection != NULL)
{
dbus_connection_close (gphoto2_backend->dbus_connection);
@@ -559,8 +577,9 @@ release_device (GVfsBackendGphoto2 *gphoto2_backend)
gphoto2_backend->hal_udi = NULL;
g_free (gphoto2_backend->hal_name);
gphoto2_backend->hal_name = NULL;
- g_free (gphoto2_backend->hal_icon_name);
- gphoto2_backend->hal_icon_name = NULL;
+#endif
+ g_free (gphoto2_backend->icon_name);
+ gphoto2_backend->icon_name = NULL;
g_free (gphoto2_backend->ignore_prefix);
gphoto2_backend->ignore_prefix = NULL;
@@ -660,13 +679,13 @@ compute_icon_name (GVfsBackendGphoto2 *gphoto2_backend)
{
char *result;
- if (gphoto2_backend->hal_icon_name == NULL)
+ if (gphoto2_backend->icon_name == NULL)
{
result = g_strdup_printf ("camera-photo");
}
else
{
- result = g_strdup (gphoto2_backend->hal_icon_name);
+ result = g_strdup (gphoto2_backend->icon_name);
}
return result;
@@ -677,8 +696,28 @@ compute_icon_name (GVfsBackendGphoto2 *gphoto2_backend)
static char *
compute_display_name (GVfsBackendGphoto2 *gphoto2_backend)
{
- char *result;
+ char *result = NULL;
+
+#ifdef HAVE_GUDEV
+ const char *s;
+
+ /* the real "nice" and user-visible name is computed in the monitor; just try
+ * using the product name here */
+ if (gphoto2_backend->udev_device != NULL)
+ {
+ s = g_udev_device_get_sysfs_attr (gphoto2_backend->udev_device, "product");
+ if (s == NULL)
+ s = g_udev_device_get_property (gphoto2_backend->udev_device, "ID_MODEL");
+ if (s != NULL)
+ result = g_strdup (s);
+ }
+ if (result == NULL )
+ {
+ /* Translator: %s represents the device, e.g. usb:001,042 */
+ result = g_strdup_printf (_("Digital Camera (%s)"), gphoto2_backend->gphoto2_port);
+ }
+#elif defined(HAVE_HAL)
if (gphoto2_backend->hal_name == NULL)
{
/* Translator: %s represents the device, e.g. usb:001,042 */
@@ -688,12 +727,76 @@ compute_display_name (GVfsBackendGphoto2 *gphoto2_backend)
{
result = g_strdup (gphoto2_backend->hal_name);
}
+#endif
return result;
}
/* ------------------------------------------------------------------------------------------------- */
+#ifdef HAVE_GUDEV
+static void
+setup_for_device (GVfsBackendGphoto2 *gphoto2_backend)
+{
+ gchar *devname;
+ char *comma;
+ char *camera_x_content_types[] = {"x-content/image-dcf", NULL};
+
+ /* turn usb:001,041 string into an udev device name */
+ if (!g_str_has_prefix (gphoto2_backend->gphoto2_port, "usb:"))
+ return;
+ devname = g_strconcat ("/dev/bus/usb/", gphoto2_backend->gphoto2_port+4, NULL);
+ if ((comma = strchr (devname, ',')) == NULL)
+ {
+ g_free (devname);
+ return;
+ }
+ *comma = '/';
+ DEBUG ("Parsed '%s' into device name %s", gphoto2_backend->gphoto2_port, devname);
+
+ /* find corresponding GUdevDevice */
+ gphoto2_backend->udev_device = g_udev_client_query_by_device_file (gphoto2_backend->gudev_client, devname);
+ g_free (devname);
+ if (gphoto2_backend->udev_device)
+ {
+ DEBUG ("-> sysfs path %s, subsys %s, name %s", g_udev_device_get_sysfs_path (gphoto2_backend->udev_device), g_udev_device_get_subsystem (gphoto2_backend->udev_device), g_udev_device_get_name (gphoto2_backend->udev_device));
+
+ /* determine icon name */
+ if (g_udev_device_has_property (gphoto2_backend->udev_device, "ID_MEDIA_PLAYER_ICON_NAME"))
+ gphoto2_backend->icon_name = g_strdup (g_udev_device_get_property (gphoto2_backend->udev_device, "ID_MEDIA_PLAYER_ICON_NAME"));
+ else if (g_udev_device_has_property (gphoto2_backend->udev_device, "ID_MEDIA_PLAYER"))
+ gphoto2_backend->icon_name = g_strdup ("multimedia-player");
+ else
+ gphoto2_backend->icon_name = g_strdup ("camera-photo");
+ }
+ else
+ DEBUG ("-> did not find matching udev device");
+
+ g_vfs_backend_set_x_content_types (G_VFS_BACKEND (gphoto2_backend), camera_x_content_types);
+}
+
+static void
+on_uevent (GUdevClient *client, gchar *action, GUdevDevice *device, gpointer user_data)
+{
+ GVfsBackendGphoto2 *gphoto2_backend = G_VFS_BACKEND_GPHOTO2 (user_data);
+
+ DEBUG ("on_uevent action %s, device %s", action, g_udev_device_get_device_file (device));
+
+ if (gphoto2_backend->udev_device != NULL &&
+ g_strcmp0 (g_udev_device_get_device_file (gphoto2_backend->udev_device), g_udev_device_get_device_file (device)) == 0 &&
+ strcmp (action, "remove") == 0)
+ {
+ DEBUG ("we have been removed!");
+
+ /* nuke all caches so we're a bit more valgrind friendly */
+ caches_invalidate_all (gphoto2_backend);
+
+ /* TODO: need a cleaner way to force unmount ourselves */
+ exit (1);
+ }
+}
+
+#elif defined(HAVE_HAL)
static void
find_udi_for_device (GVfsBackendGphoto2 *gphoto2_backend)
{
@@ -851,17 +954,17 @@ find_udi_for_device (GVfsBackendGphoto2 *gphoto2_backend)
gphoto2_backend->hal_name = name;
if (icon_from_hal != NULL)
{
- gphoto2_backend->hal_icon_name = g_strdup (icon_from_hal);
+ gphoto2_backend->icon_name = g_strdup (icon_from_hal);
}
else
{
if (m == 1)
{
- gphoto2_backend->hal_icon_name = g_strdup ("multimedia-player");
+ gphoto2_backend->icon_name = g_strdup ("multimedia-player");
}
else
{
- gphoto2_backend->hal_icon_name = g_strdup ("camera-photo");
+ gphoto2_backend->icon_name = g_strdup ("camera-photo");
}
}
@@ -909,6 +1012,7 @@ _hal_device_removed (LibHalContext *hal_ctx, const char *udi)
exit (1);
}
}
+#endif
/* ------------------------------------------------------------------------------------------------- */
@@ -1376,13 +1480,30 @@ do_mount (GVfsBackend *backend,
GPPortInfo info;
GPPortInfoList *il = NULL;
int n;
- DBusError dbus_error;
CameraStorageInformation *storage_info;
int num_storage_info;
DEBUG ("do_mount %p", gphoto2_backend);
+#ifdef HAVE_GUDEV
+ /* setup gudev */
+ const char *subsystems[] = {"usb", NULL};
+
+ gphoto2_backend->gudev_client = g_udev_client_new (subsystems);
+ if (gphoto2_backend->gudev_client == NULL)
+ {
+ release_device (gphoto2_backend);
+ g_set_error_literal (&error, G_IO_ERROR, G_IO_ERROR_FAILED, _("Cannot create gudev client"));
+ g_vfs_job_failed_from_error (G_VFS_JOB (job), error);
+ g_error_free (error);
+ return;
+ }
+
+ g_signal_connect (gphoto2_backend->gudev_client, "uevent", G_CALLBACK (on_uevent), gphoto2_backend);
+
+#elif defined(HAVE_HAL)
/* setup libhal */
+ DBusError dbus_error;
dbus_error_init (&dbus_error);
gphoto2_backend->dbus_connection = dbus_bus_get_private (DBUS_BUS_SYSTEM, &dbus_error);
@@ -1423,6 +1544,7 @@ do_mount (GVfsBackend *backend,
libhal_ctx_set_device_removed (gphoto2_backend->hal_ctx, _hal_device_removed);
libhal_ctx_set_user_data (gphoto2_backend->hal_ctx, gphoto2_backend);
+#endif
/* setup gphoto2 */
@@ -1442,7 +1564,11 @@ do_mount (GVfsBackend *backend,
DEBUG (" decoded host='%s'", gphoto2_backend->gphoto2_port);
+#ifdef HAVE_GUDEV
+ setup_for_device (gphoto2_backend);
+#elif defined(HAVE_HAL)
find_udi_for_device (gphoto2_backend);
+#endif
gphoto2_backend->context = gp_context_new ();
if (gphoto2_backend->context == NULL)
diff --git a/monitor/gphoto2/Makefile.am b/monitor/gphoto2/Makefile.am
index bbb54bc..18ab680 100644
--- a/monitor/gphoto2/Makefile.am
+++ b/monitor/gphoto2/Makefile.am
@@ -3,6 +3,10 @@ NULL =
libexec_PROGRAMS = gvfs-gphoto2-volume-monitor
+if USE_GUDEV
+gvfs_gphoto2_volume_monitor_SOURCES =
+
+else
BUILT_SOURCES = \
hal-marshal.h hal-marshal.c
@@ -12,12 +16,15 @@ hal-marshal.h: hal-marshal.list
hal-marshal.c: hal-marshal.list
echo "#include \"hal-marshal.h\"" > $@ && glib-genmarshal $< --prefix=hal_marshal --body >> $@
-
gvfs_gphoto2_volume_monitor_SOURCES = \
hal-utils.c hal-utils.h \
hal-marshal.c hal-marshal.h \
hal-device.c hal-device.h \
hal-pool.c hal-pool.h \
+ $(NULL)
+endif
+
+gvfs_gphoto2_volume_monitor_SOURCES += \
gphoto2-volume-monitor-daemon.c \
ggphoto2volume.c ggphoto2volume.h \
ggphoto2volumemonitor.c ggphoto2volumemonitor.h \
@@ -28,24 +35,36 @@ gvfs_gphoto2_volume_monitor_CFLAGS = \
-I$(top_srcdir)/common \
-I$(top_srcdir)/monitor/proxy \
$(GLIB_CFLAGS) \
- $(HAL_CFLAGS) \
$(GPHOTO2_CFLAGS) \
-DGIO_MODULE_DIR=\"$(GIO_MODULE_DIR)\" \
-DGVFS_LOCALEDIR=\""$(localedir)"\" \
-DG_DISABLE_DEPRECATED \
+ -DG_UDEV_API_IS_SUBJECT_TO_CHANGE \
$(NULL)
+if USE_GUDEV
+gvfs_gphoto2_volume_monitor_CFLAGS += $(GUDEV_CFLAGS)
+else
+gvfs_gphoto2_volume_monitor_CFLAGS += $(HAL_CFLAGS)
+endif
+
gvfs_gphoto2_volume_monitor_LDFLAGS = \
$(NULL)
gvfs_gphoto2_volume_monitor_LDADD = \
$(GLIB_LIBS) \
- $(HAL_LIBS) \
$(GPHOTO2_LIBS) \
$(top_builddir)/common/libgvfscommon.la \
$(top_builddir)/monitor/proxy/libgvfsproxyvolumemonitordaemon-noin.la \
$(NULL)
+if USE_GUDEV
+gvfs_gphoto2_volume_monitor_LDADD += $(GUDEV_LIBS)
+else
+gvfs_gphoto2_volume_monitor_LDADD += $(HAL_LIBS)
+endif
+
+
remote_volume_monitorsdir = $(datadir)/gvfs/remote-volume-monitors
remote_volume_monitors_DATA = gphoto2.monitor
diff --git a/monitor/gphoto2/ggphoto2volume.c b/monitor/gphoto2/ggphoto2volume.c
index 1ddbee1..b7c0438 100644
--- a/monitor/gphoto2/ggphoto2volume.c
+++ b/monitor/gphoto2/ggphoto2volume.c
@@ -33,7 +33,9 @@
#include "ggphoto2volume.h"
+#ifndef HAVE_GUDEV
#include "hal-utils.h"
+#endif
/* Protects all fields of GHalDrive that can change */
G_LOCK_DEFINE_STATIC(gphoto2_volume);
@@ -44,8 +46,12 @@ struct _GGPhoto2Volume {
GVolumeMonitor *volume_monitor; /* owned by volume monitor */
char *device_path;
+#ifdef HAVE_GUDEV
+ GUdevDevice *device;
+#else
HalDevice *device;
HalDevice *drive_device;
+#endif
GFile *activation_root;
@@ -138,6 +144,120 @@ dupv_and_uniqify (char **str_array)
return result;
}
+#ifdef HAVE_GUDEV
+static int hexdigit(char c)
+{
+ if (c >= 'a')
+ return c - 'a' + 10;
+ if (c >= 'A')
+ return c - 'A' + 10;
+ g_return_val_if_fail (c >= '0' && c <= '9', 0);
+ return c - '0';
+}
+
+/* Do not free result, it's a static buffer */
+static const char*
+udev_decode_string (const char* encoded)
+{
+ static char decoded[4096];
+ int len;
+ const char* s;
+
+ if (encoded == NULL)
+ return NULL;
+
+ for (len = 0, s = encoded; *s && len < sizeof(decoded)-1; ++len, ++s)
+ {
+ /* need to check for NUL terminator in advance */
+ if (s[0] == '\\' && s[1] == 'x' && s[2] >= '0' && s[3] >= '0')
+ {
+ decoded[len] = (hexdigit(s[2]) << 4) | hexdigit(s[3]);
+ s += 3;
+ }
+ else
+ decoded[len] = *s;
+ }
+ decoded[len] = '\0';
+ return decoded;
+}
+
+static void
+set_volume_name (GGPhoto2Volume *v)
+{
+ const char *gphoto_name;
+ const char *product = NULL;
+ const char *vendor;
+ const char *model;
+
+ /* our preference: ID_GPHOTO2 > ID_MEDIA_PLAYER_{VENDOR,PRODUCT} > product >
+ * ID_{VENDOR,MODEL} */
+
+ gphoto_name = g_udev_device_get_property (v->device, "ID_GPHOTO2");
+ if (gphoto_name != NULL && strcmp (gphoto_name, "1") != 0)
+ {
+ v->name = g_strdup (gphoto_name);
+ return;
+ }
+
+ vendor = g_udev_device_get_property (v->device, "ID_MEDIA_PLAYER_VENDOR");
+ if (vendor == NULL)
+ vendor = g_udev_device_get_property (v->device, "ID_VENDOR_ENC");
+ model = g_udev_device_get_property (v->device, "ID_MEDIA_PLAYER_MODEL");
+ if (model == NULL)
+ {
+ model = g_udev_device_get_property (v->device, "ID_MODEL_ENC");
+ product = g_udev_device_get_sysfs_attr (v->device, "product");
+ }
+
+ v->name = NULL;
+ if (product != NULL && strlen (product) > 0)
+ v->name = g_strdup (product);
+ else if (vendor == NULL)
+ {
+ if (model != NULL)
+ v->name = g_strdup (udev_decode_string (model));
+ }
+ else
+ {
+ if (model != NULL)
+ {
+ /* we can't call udev_decode_string() twice in one g_strdup_printf(),
+ * it returns a static buffer */
+ gchar *temp = g_strdup_printf ("%s %s", vendor, model);
+ v->name = g_strdup (udev_decode_string (temp));
+ g_free (temp);
+ }
+ else
+ {
+ if (g_udev_device_has_property (v->device, "ID_MEDIA_PLAYER"))
+ {
+ /* Translators: %s is the device vendor */
+ v->name = g_strdup_printf (_("%s Audio Player"), udev_decode_string (vendor));
+ }
+ else
+ {
+ /* Translators: %s is the device vendor */
+ v->name = g_strdup_printf (_("%s Camera"), udev_decode_string (vendor));
+ }
+ }
+ }
+
+ if (v->name == NULL)
+ v->name = g_strdup (_("Camera"));
+}
+
+static void
+set_volume_icon (GGPhoto2Volume *volume)
+{
+ if (g_udev_device_has_property (volume->device, "ID_MEDIA_PLAYER_ICON_NAME"))
+ volume->icon = g_strdup (g_udev_device_get_property (volume->device, "ID_MEDIA_PLAYER_ICON_NAME"));
+ else if (g_udev_device_has_property (volume->device, "ID_MEDIA_PLAYER"))
+ volume->icon = g_strdup ("multimedia-player");
+ else
+ volume->icon = g_strdup ("camera-photo");
+}
+
+#else
static void
do_update_from_hal_for_camera (GGPhoto2Volume *v)
{
@@ -239,23 +359,40 @@ hal_changed (HalDevice *device, const char *key, gpointer user_data)
/*g_warning ("hal modifying %s (property %s changed)", gphoto2_volume->device_path, key);*/
update_from_hal (gphoto2_volume, TRUE);
}
+#endif
GGPhoto2Volume *
g_gphoto2_volume_new (GVolumeMonitor *volume_monitor,
+#ifdef HAVE_GUDEV
+ GUdevDevice *device,
+ GUdevClient *gudev_client,
+#else
HalDevice *device,
HalPool *pool,
+#endif
GFile *activation_root)
{
GGPhoto2Volume *volume;
+#ifndef HAVE_GUDEV
HalDevice *drive_device;
const char *storage_udi;
+#endif
const char *device_path;
g_return_val_if_fail (volume_monitor != NULL, NULL);
g_return_val_if_fail (device != NULL, NULL);
+#ifdef HAVE_GUDEV
+ g_return_val_if_fail (gudev_client != NULL, NULL);
+#else
g_return_val_if_fail (pool != NULL, NULL);
+#endif
g_return_val_if_fail (activation_root != NULL, NULL);
+#ifdef HAVE_GUDEV
+ if (!g_udev_device_has_property (device, "ID_GPHOTO2"))
+ return NULL;
+ device_path = g_udev_device_get_device_file (device);
+#else
if (!(hal_device_has_capability (device, "camera") ||
(hal_device_has_capability (device, "portable_audio_player") &&
hal_device_get_property_bool (device, "camera.libgphoto2.support"))))
@@ -276,19 +413,28 @@ g_gphoto2_volume_new (GVolumeMonitor *volume_monitor,
device_path = hal_device_get_property_string (drive_device, "linux.device_file");
if (strlen (device_path) == 0)
device_path = NULL;
+#endif
volume = g_object_new (G_TYPE_GPHOTO2_VOLUME, NULL);
volume->volume_monitor = volume_monitor;
g_object_add_weak_pointer (G_OBJECT (volume_monitor), (gpointer) &(volume->volume_monitor));
volume->device_path = g_strdup (device_path);
volume->device = g_object_ref (device);
+#ifndef HAVE_GUDEV
volume->drive_device = g_object_ref (drive_device);
+#endif
volume->activation_root = g_object_ref (activation_root);
+#ifdef HAVE_GUDEV
+ set_volume_name (volume);
+ set_volume_icon (volume);
+ /* we do not really need to listen for changes */
+#else
g_signal_connect_object (device, "hal_property_changed", (GCallback) hal_changed, volume, 0);
g_signal_connect_object (drive_device, "hal_property_changed", (GCallback) hal_changed, volume, 0);
update_from_hal (volume, FALSE);
+#endif
return volume;
}
@@ -360,6 +506,24 @@ g_gphoto2_volume_get_mount (GVolume *volume)
return NULL;
}
+#ifdef HAVE_GUDEV
+gboolean
+g_gphoto2_volume_has_path (GGPhoto2Volume *volume,
+ const char *sysfs_path)
+{
+ GGPhoto2Volume *gphoto2_volume = G_GPHOTO2_VOLUME (volume);
+ gboolean res;
+
+ G_LOCK (gphoto2_volume);
+ res = FALSE;
+ if (gphoto2_volume->device != NULL)
+ res = strcmp (g_udev_device_get_sysfs_path (gphoto2_volume->device), sysfs_path) == 0;
+ G_UNLOCK (gphoto2_volume);
+ return res;
+}
+
+#else
+
gboolean
g_gphoto2_volume_has_udi (GGPhoto2Volume *volume,
const char *udi)
@@ -374,6 +538,7 @@ g_gphoto2_volume_has_udi (GGPhoto2Volume *volume,
G_UNLOCK (gphoto2_volume);
return res;
}
+#endif
typedef struct
{
@@ -449,9 +614,12 @@ g_gphoto2_volume_get_identifier (GVolume *volume,
G_LOCK (gphoto2_volume);
id = NULL;
+#ifndef HAVE_GUDEV
if (strcmp (kind, G_VOLUME_IDENTIFIER_KIND_HAL_UDI) == 0)
id = g_strdup (hal_device_get_udi (gphoto2_volume->device));
- else if (strcmp (kind, G_VOLUME_IDENTIFIER_KIND_UNIX_DEVICE) == 0)
+ else
+#endif
+ if (strcmp (kind, G_VOLUME_IDENTIFIER_KIND_UNIX_DEVICE) == 0)
id = g_strdup (gphoto2_volume->device_path);
G_UNLOCK (gphoto2_volume);
@@ -468,8 +636,10 @@ g_gphoto2_volume_enumerate_identifiers (GVolume *volume)
res = g_ptr_array_new ();
+#ifndef HAVE_GUDEV
g_ptr_array_add (res,
g_strdup (G_VOLUME_IDENTIFIER_KIND_HAL_UDI));
+#endif
if (gphoto2_volume->device_path && *gphoto2_volume->device_path != 0)
g_ptr_array_add (res,
diff --git a/monitor/gphoto2/ggphoto2volume.h b/monitor/gphoto2/ggphoto2volume.h
index a7a4131..08d9b49 100644
--- a/monitor/gphoto2/ggphoto2volume.h
+++ b/monitor/gphoto2/ggphoto2volume.h
@@ -26,7 +26,13 @@
#include <glib-object.h>
#include <gio/gio.h>
-#include "hal-pool.h"
+#ifdef HAVE_GUDEV
+ #include <gudev/gudev.h>
+#elif defined(HAVE_HAL)
+ #include "hal-pool.h"
+#else
+ #error Needs gudev or hal
+#endif
#include "ggphoto2volumemonitor.h"
G_BEGIN_DECLS
@@ -46,12 +52,22 @@ struct _GGPhoto2VolumeClass {
GType g_gphoto2_volume_get_type (void) G_GNUC_CONST;
GGPhoto2Volume *g_gphoto2_volume_new (GVolumeMonitor *volume_monitor,
+#ifdef HAVE_GUDEV
+ GUdevDevice *device,
+ GUdevClient *gudev_client,
+#else
HalDevice *device,
HalPool *pool,
+#endif
GFile *activation_root);
+#ifdef HAVE_GUDEV
+gboolean g_gphoto2_volume_has_path (GGPhoto2Volume *volume,
+ const char *path);
+#else
gboolean g_gphoto2_volume_has_udi (GGPhoto2Volume *volume,
const char *udi);
+#endif
void g_gphoto2_volume_removed (GGPhoto2Volume *volume);
diff --git a/monitor/gphoto2/ggphoto2volumemonitor.c b/monitor/gphoto2/ggphoto2volumemonitor.c
index b5ec0ec..a51674c 100644
--- a/monitor/gphoto2/ggphoto2volumemonitor.c
+++ b/monitor/gphoto2/ggphoto2volumemonitor.c
@@ -35,25 +35,42 @@
#include "ggphoto2volumemonitor.h"
#include "ggphoto2volume.h"
+#ifdef HAVE_GUDEV
+#include <gio/gio.h>
+#include <gio/gunixmounts.h>
+#else
#include "hal-pool.h"
+#endif
G_LOCK_DEFINE_STATIC(hal_vm);
static GGPhoto2VolumeMonitor *the_volume_monitor = NULL;
+#ifndef HAVE_GUDEV
static HalPool *pool = NULL;
+#endif
struct _GGPhoto2VolumeMonitor {
GNativeVolumeMonitor parent;
GUnixMountMonitor *mount_monitor;
+#ifdef HAVE_GUDEV
+ GUdevClient *gudev_client;
+#else
HalPool *pool;
+#endif
GList *last_camera_devices;
GList *camera_volumes;
};
+#ifdef HAVE_GUDEV
+static void on_uevent (GUdevClient *client,
+ gchar *action,
+ GUdevDevice *device,
+ gpointer user_data);
+#else
static void hal_changed (HalPool *pool,
HalDevice *device,
gpointer user_data);
@@ -64,7 +81,9 @@ static void update_all (GGPhoto2VolumeMonitor *monitor,
static void update_cameras (GGPhoto2VolumeMonitor *monitor,
GList **added_volumes,
GList **removed_volumes);
+#endif
+static GList* get_stores_for_camera (int bus_num, int device_num);
G_DEFINE_TYPE (GGPhoto2VolumeMonitor, g_gphoto2_volume_monitor, G_TYPE_VOLUME_MONITOR)
@@ -75,6 +94,7 @@ list_free (GList *objects)
g_list_free (objects);
}
+#ifndef HAVE_GUDEV
static HalPool *
get_hal_pool (void)
{
@@ -85,6 +105,7 @@ get_hal_pool (void)
return pool;
}
+#endif
static void
g_gphoto2_volume_monitor_dispose (GObject *object)
@@ -108,9 +129,14 @@ g_gphoto2_volume_monitor_finalize (GObject *object)
monitor = G_GPHOTO2_VOLUME_MONITOR (object);
- g_signal_handlers_disconnect_by_func (monitor->pool, hal_changed, monitor);
+#ifdef HAVE_GUDEV
+ g_signal_handlers_disconnect_by_func (monitor->gudev_client, on_uevent, monitor);
+ g_object_unref (monitor->gudev_client);
+#else
+ g_signal_handlers_disconnect_by_func (monitor->pool, hal_changed, monitor);
g_object_unref (monitor->pool);
+#endif
list_free (monitor->last_camera_devices);
list_free (monitor->camera_volumes);
@@ -161,6 +187,145 @@ get_mount_for_uuid (GVolumeMonitor *volume_monitor, const char *uuid)
return NULL;
}
+#ifdef HAVE_GUDEV
+
+static void
+gudev_add_camera (GGPhoto2VolumeMonitor *monitor, GUdevDevice *device, gboolean do_emit)
+{
+ GGPhoto2Volume *volume;
+ GList *store_heads, *l;
+ guint num_store_heads;
+ const char *property;
+ int usb_bus_num;
+ int usb_device_num;
+
+ property = g_udev_device_get_property (device, "BUSNUM");
+ if (property == NULL) {
+ g_warning("device %s has no BUSNUM property, ignoring", g_udev_device_get_device_file (device));
+ return;
+ }
+ usb_bus_num = atoi (property);
+
+ property = g_udev_device_get_property (device, "DEVNUM");
+ if (property == NULL) {
+ g_warning("device %s has no DEVNUM property, ignoring", g_udev_device_get_device_file (device));
+ return;
+ }
+ usb_device_num = atoi (property);
+
+ /* g_debug ("gudev_add_camera: camera device %s (bus: %i, device: %i)",
+ g_udev_device_get_device_file (device),
+ usb_bus_num, usb_device_num); */
+
+ store_heads = get_stores_for_camera (usb_bus_num, usb_device_num);
+ num_store_heads = g_list_length (store_heads);
+ for (l = store_heads ; l != NULL; l = l->next)
+ {
+ char *store_path = (char *) l->data;
+ GFile *activation_mount_root;
+ gchar *uri;
+
+ /* If we only have a single store, don't use the store name at all. The backend automatically
+ * prepend the storename; this is to work around bugs with devices (like the iPhone) for which
+ * the store name changes every time the camera is initialized (e.g. mounted).
+ */
+ if (num_store_heads == 1)
+ {
+ uri = g_strdup_printf ("gphoto2://[usb:%03d,%03d]", usb_bus_num, usb_device_num);
+ }
+ else
+ {
+ uri = g_strdup_printf ("gphoto2://[usb:%03d,%03d]/%s", usb_bus_num, usb_device_num,
+ store_path[0] == '/' ? store_path + 1 : store_path);
+ }
+ /* g_debug ("gudev_add_camera: ... adding URI for storage head: %s", uri); */
+ activation_mount_root = g_file_new_for_uri (uri);
+ g_free (uri);
+
+ volume = g_gphoto2_volume_new (G_VOLUME_MONITOR (monitor),
+ device,
+ monitor->gudev_client,
+ activation_mount_root);
+ if (volume != NULL)
+ {
+ monitor->camera_volumes = g_list_prepend (monitor->camera_volumes, volume);
+ if (do_emit)
+ g_signal_emit_by_name (monitor, "volume_added", volume);
+ }
+
+ if (activation_mount_root != NULL)
+ g_object_unref (activation_mount_root);
+ }
+
+ g_list_foreach (store_heads, (GFunc) g_free, NULL);
+ g_list_free (store_heads);
+}
+
+static void
+gudev_remove_camera (GGPhoto2VolumeMonitor *monitor, GUdevDevice *device)
+{
+ /* g_debug ("gudev_remove_camera: %s", g_udev_device_get_device_file (device)); */
+
+ GList *l;
+ const gchar* sysfs_path = g_udev_device_get_sysfs_path (device);
+
+ for (l = monitor->camera_volumes; l != NULL; l = l->next)
+ {
+ GGPhoto2Volume *volume = G_GPHOTO2_VOLUME (l->data);
+
+ if (g_gphoto2_volume_has_path (volume, sysfs_path))
+ {
+ /* g_debug ("gudev_remove_camera: found volume %s, deleting", sysfs_path); */
+ g_signal_emit_by_name (monitor, "volume_removed", volume);
+ g_signal_emit_by_name (volume, "removed");
+ g_gphoto2_volume_removed (volume);
+ monitor->camera_volumes = g_list_remove (monitor->camera_volumes, volume);
+ g_object_unref (volume);
+ }
+ }
+}
+
+static void
+on_uevent (GUdevClient *client,
+ gchar *action,
+ GUdevDevice *device,
+ gpointer user_data)
+{
+ GGPhoto2VolumeMonitor *monitor = G_GPHOTO2_VOLUME_MONITOR (user_data);
+
+ /* g_debug ("on_uevent: action=%s, device=%s", action, g_udev_device_get_device_file(device)); */
+
+ /* filter out uninteresting events */
+ if (!g_udev_device_has_property (device, "ID_GPHOTO2"))
+ {
+ /* g_debug ("on_uevent: discarding, not ID_GPHOTO2"); */
+ return;
+ }
+
+ if (strcmp (action, "add") == 0)
+ gudev_add_camera (monitor, device, TRUE);
+ else if (strcmp (action, "remove") == 0)
+ gudev_remove_camera (monitor, device);
+}
+
+/* Find all attached gphoto supported cameras; this is called on startup
+ * (coldplugging). */
+static void
+gudev_coldplug_cameras (GGPhoto2VolumeMonitor *monitor)
+{
+ GList *usb_devices, *l;
+
+ usb_devices = g_udev_client_query_by_subsystem (monitor->gudev_client, "usb");
+ for (l = usb_devices; l != NULL; l = l->next)
+ {
+ GUdevDevice *d = l->data;
+ if (g_udev_device_has_property (d, "ID_GPHOTO2"))
+ gudev_add_camera (monitor, d, FALSE);
+ }
+}
+
+#else
+
static void
hal_changed (HalPool *pool,
HalDevice *device,
@@ -172,6 +337,7 @@ hal_changed (HalPool *pool,
update_all (monitor, TRUE);
}
+#endif
static GObject *
g_gphoto2_volume_monitor_constructor (GType type,
@@ -204,6 +370,18 @@ g_gphoto2_volume_monitor_constructor (GType type,
construct_properties);
monitor = G_GPHOTO2_VOLUME_MONITOR (object);
+
+#ifdef HAVE_GUDEV
+ const char *subsystems[] = {"usb", NULL};
+ monitor->gudev_client = g_udev_client_new (subsystems);
+
+ g_signal_connect (monitor->gudev_client,
+ "uevent", G_CALLBACK (on_uevent),
+ monitor);
+
+ gudev_coldplug_cameras (monitor);
+
+#else
monitor->pool = g_object_ref (get_hal_pool ());
g_signal_connect (monitor->pool,
@@ -215,6 +393,7 @@ g_gphoto2_volume_monitor_constructor (GType type,
monitor);
update_all (monitor, FALSE);
+#endif
G_LOCK (hal_vm);
the_volume_monitor = monitor;
@@ -231,7 +410,13 @@ g_gphoto2_volume_monitor_init (GGPhoto2VolumeMonitor *monitor)
static gboolean
is_supported (void)
{
+#ifdef HAVE_GUDEV
+ /* Today's Linux desktops pretty much need udev to have anything working, so
+ * assume it's there */
+ return TRUE;
+#else
return get_hal_pool() != NULL;
+#endif
}
static void
@@ -267,6 +452,7 @@ g_gphoto2_volume_monitor_new (void)
return G_VOLUME_MONITOR (monitor);
}
+#ifndef HAVE_GUDEV
static void
diff_sorted_lists (GList *list1,
GList *list2,
@@ -405,6 +591,7 @@ update_all (GGPhoto2VolumeMonitor *monitor,
list_free (added_volumes);
}
}
+#endif
static GList *
get_stores_for_camera (int bus_num, int device_num)
@@ -482,6 +669,7 @@ out:
return l;
}
+#ifndef HAVE_GUDEV
static void
update_cameras (GGPhoto2VolumeMonitor *monitor,
GList **added_volumes,
@@ -618,3 +806,4 @@ update_cameras (GGPhoto2VolumeMonitor *monitor,
list_free (monitor->last_camera_devices);
monitor->last_camera_devices = new_camera_devices;
}
+#endif
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]