diff options
author | Philip Langdale <philipl@overt.org> | 2012-08-04 16:54:26 -0700 |
---|---|---|
committer | Philip Langdale <philipl@overt.org> | 2013-01-11 20:29:54 -0800 |
commit | e660be080ce67a10c962ad416e215b71f575e820 (patch) | |
tree | cd6e8aab2e6f0cc27588f7246044b90434c05ac5 /monitor | |
parent | 382c9014f2dbcb9076568f3599a514608a3e1d0a (diff) | |
download | gvfs-e660be080ce67a10c962ad416e215b71f575e820.tar.gz |
MTP: Initial MTP volume monitor implementation.
Diffstat (limited to 'monitor')
-rw-r--r-- | monitor/Makefile.am | 4 | ||||
-rw-r--r-- | monitor/mtp/Makefile.am | 51 | ||||
-rw-r--r-- | monitor/mtp/gmtpvolume.c | 453 | ||||
-rw-r--r-- | monitor/mtp/gmtpvolume.h | 60 | ||||
-rw-r--r-- | monitor/mtp/gmtpvolumemonitor.c | 342 | ||||
-rw-r--r-- | monitor/mtp/gmtpvolumemonitor.h | 54 | ||||
-rw-r--r-- | monitor/mtp/mtp-volume-monitor-daemon.c | 41 | ||||
-rw-r--r-- | monitor/mtp/mtp.monitor | 4 | ||||
-rw-r--r-- | monitor/mtp/org.gtk.Private.MTPVolumeMonitor.service.in | 3 |
9 files changed, 1010 insertions, 2 deletions
diff --git a/monitor/Makefile.am b/monitor/Makefile.am index d903df1f..9bf4e8d9 100644 --- a/monitor/Makefile.am +++ b/monitor/Makefile.am @@ -1,5 +1,5 @@ -DIST_SUBDIRS = proxy hal gdu gphoto2 afc udisks2 -SUBDIRS = proxy +DIST_SUBDIRS = proxy hal gdu gphoto2 afc udisks2 mtp +SUBDIRS = proxy mtp if USE_HAL SUBDIRS += hal diff --git a/monitor/mtp/Makefile.am b/monitor/mtp/Makefile.am new file mode 100644 index 00000000..2f585c65 --- /dev/null +++ b/monitor/mtp/Makefile.am @@ -0,0 +1,51 @@ + +NULL = + +libexec_PROGRAMS = gvfs-mtp-volume-monitor + +gvfs_mtp_volume_monitor_SOURCES = + +gvfs_mtp_volume_monitor_SOURCES += \ + mtp-volume-monitor-daemon.c \ + gmtpvolume.c gmtpvolume.h \ + gmtpvolumemonitor.c gmtpvolumemonitor.h \ + $(NULL) + +gvfs_mtp_volume_monitor_CFLAGS = \ + -DG_LOG_DOMAIN=\"GVFS-MTP\" \ + -I$(top_srcdir)/common \ + -I$(top_srcdir)/monitor/proxy \ + $(GLIB_CFLAGS) \ + -DGIO_MODULE_DIR=\"$(GIO_MODULE_DIR)\" \ + -DGVFS_LOCALEDIR=\""$(localedir)"\" \ + -DG_UDEV_API_IS_SUBJECT_TO_CHANGE \ + $(NULL) + +gvfs_mtp_volume_monitor_CFLAGS += $(GUDEV_CFLAGS) + +gvfs_mtp_volume_monitor_LDFLAGS = \ + $(NULL) + +gvfs_mtp_volume_monitor_LDADD = \ + $(GLIB_LIBS) \ + $(top_builddir)/common/libgvfscommon.la \ + $(top_builddir)/monitor/proxy/libgvfsproxyvolumemonitordaemon-noin.la \ + $(NULL) + +gvfs_mtp_volume_monitor_LDADD += $(GUDEV_LIBS) + + +remote_volume_monitorsdir = $(datadir)/gvfs/remote-volume-monitors +remote_volume_monitors_DATA = mtp.monitor + +servicedir = $(datadir)/dbus-1/services +service_in_files = org.gtk.Private.MTPVolumeMonitor.service.in +service_DATA = $(service_in_files:.service.in=.service) + +$(service_DATA): $(service_in_files) Makefile + $(AM_V_GEN) $(SED) -e "s|\@libexecdir\@|$(libexecdir)|" $< > $@ + +clean-local: + rm -f *~ *.loT $(service_DATA) + +EXTRA_DIST = $(service_in_files) mtp.monitor diff --git a/monitor/mtp/gmtpvolume.c b/monitor/mtp/gmtpvolume.c new file mode 100644 index 00000000..2bb87aa6 --- /dev/null +++ b/monitor/mtp/gmtpvolume.c @@ -0,0 +1,453 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ +/* GIO - GLib Input, Output and Streaming Library + * + * Copyright (C) 2006-2007 Red Hat, Inc. + * + * 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 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., 59 Temple Place, Suite 330, + * Boston, MA 02111-1307, USA. + * + * Author: David Zeuthen <davidz@redhat.com> + */ + +#include <config.h> + +#include <string.h> +#include <sys/wait.h> +#include <unistd.h> + +#include <glib.h> +#include <glib/gi18n-lib.h> +#include <gio/gio.h> + +#include "gmtpvolume.h" + +#ifndef HAVE_GUDEV +#include "hal-utils.h" +#endif + +/* Protects all fields of GHalDrive that can change */ +G_LOCK_DEFINE_STATIC(mtp_volume); + +struct _GMtpVolume { + GObject parent; + + GVolumeMonitor *volume_monitor; /* owned by volume monitor */ + + char *device_path; + GUdevDevice *device; + + GFile *activation_root; + + char *name; + char *icon; +}; + +static void g_mtp_volume_volume_iface_init (GVolumeIface *iface); + +G_DEFINE_TYPE_EXTENDED (GMtpVolume, g_mtp_volume, G_TYPE_OBJECT, 0, + G_IMPLEMENT_INTERFACE (G_TYPE_VOLUME, + g_mtp_volume_volume_iface_init)) + +static void +g_mtp_volume_finalize (GObject *object) +{ + GMtpVolume *volume; + + volume = G_MTP_VOLUME (object); + + if (volume->device != NULL) + g_object_unref (volume->device); + + if (volume->activation_root != NULL) + g_object_unref (volume->activation_root); + + if (volume->volume_monitor != NULL) + g_object_remove_weak_pointer (G_OBJECT (volume->volume_monitor), (gpointer) &(volume->volume_monitor)); + + g_free (volume->name); + g_free (volume->icon); + + if (G_OBJECT_CLASS (g_mtp_volume_parent_class)->finalize) + (*G_OBJECT_CLASS (g_mtp_volume_parent_class)->finalize) (object); +} + +static void +g_mtp_volume_class_init (GMtpVolumeClass *klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + + gobject_class->finalize = g_mtp_volume_finalize; +} + +static void +g_mtp_volume_init (GMtpVolume *mtp_volume) +{ +} + +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 (GMtpVolume *v) +{ + const char *gphoto_name; + const char *product = NULL; + const char *vendor; + const char *model; + + /* our preference: ID_MTP > ID_MEDIA_PLAYER_{VENDOR,PRODUCT} > product > + * ID_{VENDOR,MODEL} */ + + gphoto_name = g_udev_device_get_property (v->device, "ID_MTP"); + 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 (GMtpVolume *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"); +} + +GMtpVolume * +g_mtp_volume_new (GVolumeMonitor *volume_monitor, + GUdevDevice *device, + GUdevClient *gudev_client, + GFile *activation_root) +{ + GMtpVolume *volume; + const char *device_path; + + g_return_val_if_fail (volume_monitor != NULL, NULL); + g_return_val_if_fail (device != NULL, NULL); + g_return_val_if_fail (gudev_client != NULL, NULL); + g_return_val_if_fail (activation_root != NULL, NULL); + + if (!g_udev_device_has_property (device, "ID_MTP_DEVICE")) + return NULL; + device_path = g_udev_device_get_device_file (device); + + volume = g_object_new (G_TYPE_MTP_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); + volume->activation_root = g_object_ref (activation_root); + + set_volume_name (volume); + set_volume_icon (volume); + /* we do not really need to listen for changes */ + + return volume; +} + +void +g_mtp_volume_removed (GMtpVolume *volume) +{ +} + +static GIcon * +g_mtp_volume_get_icon (GVolume *volume) +{ + GMtpVolume *mtp_volume = G_MTP_VOLUME (volume); + GIcon *icon; + + G_LOCK (mtp_volume); + icon = g_themed_icon_new (mtp_volume->icon); + G_UNLOCK (mtp_volume); + return icon; +} + +static char * +g_mtp_volume_get_name (GVolume *volume) +{ + GMtpVolume *mtp_volume = G_MTP_VOLUME (volume); + char *name; + + G_LOCK (mtp_volume); + name = g_strdup (mtp_volume->name); + G_UNLOCK (mtp_volume); + + return name; +} + +static char * +g_mtp_volume_get_uuid (GVolume *volume) +{ + return NULL; +} + +static gboolean +g_mtp_volume_can_mount (GVolume *volume) +{ + return TRUE; +} + +static gboolean +g_mtp_volume_can_eject (GVolume *volume) +{ + return FALSE; +} + +static gboolean +g_mtp_volume_should_automount (GVolume *volume) +{ + return TRUE; +} + +static GDrive * +g_mtp_volume_get_drive (GVolume *volume) +{ + return NULL; +} + +static GMount * +g_mtp_volume_get_mount (GVolume *volume) +{ + return NULL; +} + +gboolean +g_mtp_volume_has_path (GMtpVolume *volume, + const char *sysfs_path) +{ + GMtpVolume *mtp_volume = G_MTP_VOLUME (volume); + gboolean res; + + G_LOCK (mtp_volume); + res = FALSE; + if (mtp_volume->device != NULL) + res = strcmp (g_udev_device_get_sysfs_path (mtp_volume->device), sysfs_path) == 0; + G_UNLOCK (mtp_volume); + return res; +} + +typedef struct +{ + GMtpVolume *enclosing_volume; + GAsyncReadyCallback callback; + gpointer user_data; +} ActivationMountOp; + +static void +mount_callback (GObject *source_object, + GAsyncResult *res, + gpointer user_data) +{ + ActivationMountOp *data = user_data; + data->callback (G_OBJECT (data->enclosing_volume), res, data->user_data); + g_free (data); +} + +static void +g_mtp_volume_mount (GVolume *volume, + GMountMountFlags flags, + GMountOperation *mount_operation, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + GMtpVolume *mtp_volume = G_MTP_VOLUME (volume); + ActivationMountOp *data; + + /*g_warning ("mtp_volume_mount (can_mount=%d foreign=%p device_path=%s)", + g_mtp_volume_can_mount (volume), + mtp_volume->activation_root, + mtp_volume->device_path);*/ + + G_LOCK (mtp_volume); + + data = g_new0 (ActivationMountOp, 1); + data->enclosing_volume = mtp_volume; + data->callback = callback; + data->user_data = user_data; + + g_file_mount_enclosing_volume (mtp_volume->activation_root, + 0, + mount_operation, + cancellable, + mount_callback, + data); + + G_UNLOCK (mtp_volume); +} + +static gboolean +g_mtp_volume_mount_finish (GVolume *volume, + GAsyncResult *result, + GError **error) +{ + GMtpVolume *mtp_volume = G_MTP_VOLUME (volume); + gboolean res; + + G_LOCK (mtp_volume); + res = g_file_mount_enclosing_volume_finish (mtp_volume->activation_root, result, error); + G_UNLOCK (mtp_volume); + + return res; +} + +static char * +g_mtp_volume_get_identifier (GVolume *volume, + const char *kind) +{ + GMtpVolume *mtp_volume = G_MTP_VOLUME (volume); + char *id; + + G_LOCK (mtp_volume); + id = NULL; + if (strcmp (kind, G_VOLUME_IDENTIFIER_KIND_UNIX_DEVICE) == 0) + id = g_strdup (mtp_volume->device_path); + G_UNLOCK (mtp_volume); + + return id; +} + +static char ** +g_mtp_volume_enumerate_identifiers (GVolume *volume) +{ + GMtpVolume *mtp_volume = G_MTP_VOLUME (volume); + GPtrArray *res; + + G_LOCK (mtp_volume); + + res = g_ptr_array_new (); + + if (mtp_volume->device_path && *mtp_volume->device_path != 0) + g_ptr_array_add (res, + g_strdup (G_VOLUME_IDENTIFIER_KIND_UNIX_DEVICE)); + + /* Null-terminate */ + g_ptr_array_add (res, NULL); + + G_UNLOCK (mtp_volume); + + return (char **)g_ptr_array_free (res, FALSE); +} + +static GFile * +g_mtp_volume_get_activation_root (GVolume *volume) +{ + GMtpVolume *mtp_volume = G_MTP_VOLUME (volume); + + return g_object_ref (mtp_volume->activation_root); +} + +static void +g_mtp_volume_volume_iface_init (GVolumeIface *iface) +{ + iface->get_name = g_mtp_volume_get_name; + iface->get_icon = g_mtp_volume_get_icon; + iface->get_uuid = g_mtp_volume_get_uuid; + iface->get_drive = g_mtp_volume_get_drive; + iface->get_mount = g_mtp_volume_get_mount; + iface->can_mount = g_mtp_volume_can_mount; + iface->can_eject = g_mtp_volume_can_eject; + iface->should_automount = g_mtp_volume_should_automount; + iface->mount_fn = g_mtp_volume_mount; + iface->mount_finish = g_mtp_volume_mount_finish; + iface->eject = NULL; + iface->eject_finish = NULL; + iface->get_identifier = g_mtp_volume_get_identifier; + iface->enumerate_identifiers = g_mtp_volume_enumerate_identifiers; + iface->get_activation_root = g_mtp_volume_get_activation_root; +} diff --git a/monitor/mtp/gmtpvolume.h b/monitor/mtp/gmtpvolume.h new file mode 100644 index 00000000..5c2ea821 --- /dev/null +++ b/monitor/mtp/gmtpvolume.h @@ -0,0 +1,60 @@ +/* GIO - GLib Input, Output and Streaming Library + * + * Copyright (C) 2006-2007 Red Hat, Inc. + * + * 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 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., 59 Temple Place, Suite 330, + * Boston, MA 02111-1307, USA. + * + * Author: David Zeuthen <davidz@redhat.com> + */ + +#ifndef __G_MTP_VOLUME_H__ +#define __G_MTP_VOLUME_H__ + +#include <glib-object.h> +#include <gio/gio.h> + +#include <gudev/gudev.h> +#include "gmtpvolumemonitor.h" + +G_BEGIN_DECLS + +#define G_TYPE_MTP_VOLUME (g_mtp_volume_get_type ()) +#define G_MTP_VOLUME(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), G_TYPE_MTP_VOLUME, GMtpVolume)) +#define G_MTP_VOLUME_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), G_TYPE_MTP_VOLUME, GMtpVolumeClass)) +#define G_IS_MTP_VOLUME(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), G_TYPE_MTP_VOLUME)) +#define G_IS_MTP_VOLUME_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), G_TYPE_MTP_VOLUME)) + +typedef struct _GMtpVolumeClass GMtpVolumeClass; + +struct _GMtpVolumeClass { + GObjectClass parent_class; +}; + +GType g_mtp_volume_get_type (void) G_GNUC_CONST; + +GMtpVolume *g_mtp_volume_new (GVolumeMonitor *volume_monitor, + GUdevDevice *device, + GUdevClient *gudev_client, + GFile *activation_root); + +gboolean g_mtp_volume_has_path (GMtpVolume *volume, + const char *path); + +void g_mtp_volume_removed (GMtpVolume *volume); + +G_END_DECLS + +#endif /* __G_MTP_VOLUME_H__ */ diff --git a/monitor/mtp/gmtpvolumemonitor.c b/monitor/mtp/gmtpvolumemonitor.c new file mode 100644 index 00000000..04996270 --- /dev/null +++ b/monitor/mtp/gmtpvolumemonitor.c @@ -0,0 +1,342 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ +/* GIO - GLib Input, Output and Streaming Library + * + * Copyright (C) 2006-2007 Red Hat, Inc. + * + * 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 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., 59 Temple Place, Suite 330, + * Boston, MA 02111-1307, USA. + * + * Author: David Zeuthen <davidz@redhat.com> + */ + +#include <config.h> + +#include <limits.h> +#include <string.h> +#include <stdlib.h> + +#include <glib.h> +#include <glib/gi18n-lib.h> +#include <gio/gio.h> + +#include "gmtpvolumemonitor.h" +#include "gmtpvolume.h" + +#include <gio/gunixmounts.h> + +G_LOCK_DEFINE_STATIC(hal_vm); + +static GMtpVolumeMonitor *the_volume_monitor = NULL; + +struct _GMtpVolumeMonitor { + GNativeVolumeMonitor parent; + + GUnixMountMonitor *mount_monitor; + + GUdevClient *gudev_client; + + GList *last_devices; + + GList *device_volumes; +}; + +static void on_uevent (GUdevClient *client, + gchar *action, + GUdevDevice *device, + gpointer user_data); + +G_DEFINE_TYPE (GMtpVolumeMonitor, g_mtp_volume_monitor, G_TYPE_VOLUME_MONITOR) + +static void +list_free (GList *objects) +{ + g_list_foreach (objects, (GFunc)g_object_unref, NULL); + g_list_free (objects); +} + +static void +g_mtp_volume_monitor_dispose (GObject *object) +{ + G_LOCK (hal_vm); + the_volume_monitor = NULL; + G_UNLOCK (hal_vm); + + if (G_OBJECT_CLASS (g_mtp_volume_monitor_parent_class)->dispose) + (*G_OBJECT_CLASS (g_mtp_volume_monitor_parent_class)->dispose) (object); +} + +static void +g_mtp_volume_monitor_finalize (GObject *object) +{ + GMtpVolumeMonitor *monitor; + + monitor = G_MTP_VOLUME_MONITOR (object); + + g_signal_handlers_disconnect_by_func (monitor->gudev_client, on_uevent, monitor); + + g_object_unref (monitor->gudev_client); + + list_free (monitor->last_devices); + list_free (monitor->device_volumes); + + if (G_OBJECT_CLASS (g_mtp_volume_monitor_parent_class)->finalize) + (*G_OBJECT_CLASS (g_mtp_volume_monitor_parent_class)->finalize) (object); +} + +static GList * +get_mounts (GVolumeMonitor *volume_monitor) +{ + return NULL; +} + +static GList * +get_volumes (GVolumeMonitor *volume_monitor) +{ + GMtpVolumeMonitor *monitor; + GList *l; + + monitor = G_MTP_VOLUME_MONITOR (volume_monitor); + + G_LOCK (hal_vm); + + l = g_list_copy (monitor->device_volumes); + g_list_foreach (l, (GFunc)g_object_ref, NULL); + + G_UNLOCK (hal_vm); + + return l; +} + +static GList * +get_connected_drives (GVolumeMonitor *volume_monitor) +{ + return NULL; +} + +static GVolume * +get_volume_for_uuid (GVolumeMonitor *volume_monitor, const char *uuid) +{ + return NULL; +} + +static GMount * +get_mount_for_uuid (GVolumeMonitor *volume_monitor, const char *uuid) +{ + return NULL; +} + +static void +gudev_add_device (GMtpVolumeMonitor *monitor, GUdevDevice *device, gboolean do_emit) +{ + GMtpVolume *volume; + GList *store_heads, *l; + guint num_store_heads; + const char *usb_bus_num, *usb_device_num; + + usb_bus_num = g_udev_device_get_property (device, "BUSNUM"); + if (usb_bus_num == NULL) { + g_warning("device %s has no BUSNUM property, ignoring", g_udev_device_get_device_file (device)); + return; + } + + usb_device_num = g_udev_device_get_property (device, "DEVNUM"); + if (usb_device_num == NULL) { + g_warning("device %s has no DEVNUM property, ignoring", g_udev_device_get_device_file (device)); + return; + } + + g_print ("gudev_add_device: device %s (bus: %i, device: %i)", + g_udev_device_get_device_file (device), + usb_bus_num, usb_device_num); + + gchar *uri = g_strdup_printf("mtp://[usb:%s,%s]", usb_bus_num, usb_device_num); + GFile *activation_mount_root; + + activation_mount_root = g_file_new_for_uri (uri); + g_free (uri); + + volume = g_mtp_volume_new (G_VOLUME_MONITOR (monitor), + device, + monitor->gudev_client, + activation_mount_root); + if (volume != NULL) + { + monitor->device_volumes = g_list_prepend (monitor->device_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); +} + +static void +gudev_remove_device (GMtpVolumeMonitor *monitor, GUdevDevice *device) +{ + GList *l, *ll; + const gchar* sysfs_path; + + sysfs_path = g_udev_device_get_sysfs_path (device); + + /* g_debug ("gudev_remove_device: %s", g_udev_device_get_device_file (device)); */ + + for (l = monitor->device_volumes; l != NULL; l = ll) + { + GMtpVolume *volume = G_MTP_VOLUME (l->data); + + ll = l->next; + + if (g_mtp_volume_has_path (volume, sysfs_path)) + { + /* g_debug ("gudev_remove_device: found volume %s, deleting", sysfs_path); */ + g_signal_emit_by_name (monitor, "volume_removed", volume); + g_signal_emit_by_name (volume, "removed"); + g_mtp_volume_removed (volume); + monitor->device_volumes = g_list_remove (monitor->device_volumes, volume); + g_object_unref (volume); + } + } +} + +static void +on_uevent (GUdevClient *client, + gchar *action, + GUdevDevice *device, + gpointer user_data) +{ + GMtpVolumeMonitor *monitor = G_MTP_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_MTP_DEVICE")) + { + /* g_debug ("on_uevent: discarding, not ID_MTP"); */ + return; + } + + if (strcmp (action, "add") == 0) + gudev_add_device (monitor, device, TRUE); + else if (strcmp (action, "remove") == 0) + gudev_remove_device (monitor, device); +} + +static void +gudev_coldplug_devices (GMtpVolumeMonitor *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_MTP_DEVICE")) + gudev_add_device (monitor, d, FALSE); + } +} + +static GObject * +g_mtp_volume_monitor_constructor (GType type, + guint n_construct_properties, + GObjectConstructParam *construct_properties) +{ + GObject *object; + GMtpVolumeMonitor *monitor; + GMtpVolumeMonitorClass *klass; + GObjectClass *parent_class; + + G_LOCK (hal_vm); + if (the_volume_monitor != NULL) + { + object = g_object_ref (the_volume_monitor); + G_UNLOCK (hal_vm); + return object; + } + G_UNLOCK (hal_vm); + + /*g_warning ("creating hal vm");*/ + + object = NULL; + + /* Invoke parent constructor. */ + klass = G_MTP_VOLUME_MONITOR_CLASS (g_type_class_peek (G_TYPE_MTP_VOLUME_MONITOR)); + parent_class = G_OBJECT_CLASS (g_type_class_peek_parent (klass)); + object = parent_class->constructor (type, + n_construct_properties, + construct_properties); + + monitor = G_MTP_VOLUME_MONITOR (object); + + 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_devices (monitor); + + G_LOCK (hal_vm); + the_volume_monitor = monitor; + G_UNLOCK (hal_vm); + + return object; +} + +static void +g_mtp_volume_monitor_init (GMtpVolumeMonitor *monitor) +{ +} + +static gboolean +is_supported (void) +{ + /* Today's Linux desktops pretty much need udev to have anything working, so + * assume it's there */ + return TRUE; +} + +static void +g_mtp_volume_monitor_class_init (GMtpVolumeMonitorClass *klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + GVolumeMonitorClass *monitor_class = G_VOLUME_MONITOR_CLASS (klass); + + gobject_class->constructor = g_mtp_volume_monitor_constructor; + gobject_class->finalize = g_mtp_volume_monitor_finalize; + gobject_class->dispose = g_mtp_volume_monitor_dispose; + + monitor_class->get_mounts = get_mounts; + monitor_class->get_volumes = get_volumes; + monitor_class->get_connected_drives = get_connected_drives; + monitor_class->get_volume_for_uuid = get_volume_for_uuid; + monitor_class->get_mount_for_uuid = get_mount_for_uuid; + monitor_class->is_supported = is_supported; +} + +/** + * g_mtp_volume_monitor_new: + * + * Returns: a new #GVolumeMonitor. + **/ +GVolumeMonitor * +g_mtp_volume_monitor_new (void) +{ + GMtpVolumeMonitor *monitor; + + monitor = g_object_new (G_TYPE_MTP_VOLUME_MONITOR, NULL); + + return G_VOLUME_MONITOR (monitor); +} diff --git a/monitor/mtp/gmtpvolumemonitor.h b/monitor/mtp/gmtpvolumemonitor.h new file mode 100644 index 00000000..fcf01409 --- /dev/null +++ b/monitor/mtp/gmtpvolumemonitor.h @@ -0,0 +1,54 @@ +/* GIO - GLib Input, Output and Streaming Library + * + * Copyright (C) 2006-2007 Red Hat, Inc. + * + * 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 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., 59 Temple Place, Suite 330, + * Boston, MA 02111-1307, USA. + * + * Author: David Zeuthen <davidz@redhat.com> + */ + +#ifndef __G_MTP_VOLUME_MONITOR_H__ +#define __G_MTP_VOLUME_MONITOR_H__ + +#include <glib-object.h> +#include <gio/gio.h> + +G_BEGIN_DECLS + +#define G_TYPE_MTP_VOLUME_MONITOR (g_mtp_volume_monitor_get_type ()) +#define G_MTP_VOLUME_MONITOR(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), G_TYPE_MTP_VOLUME_MONITOR, GMtpVolumeMonitor)) +#define G_MTP_VOLUME_MONITOR_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), G_TYPE_MTP_VOLUME_MONITOR, GMtpVolumeMonitorClass)) +#define G_IS_MTP_VOLUME_MONITOR(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), G_TYPE_MTP_VOLUME_MONITOR)) +#define G_IS_MTP_VOLUME_MONITOR_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), G_TYPE_MTP_VOLUME_MONITOR)) + +typedef struct _GMtpVolumeMonitor GMtpVolumeMonitor; +typedef struct _GMtpVolumeMonitorClass GMtpVolumeMonitorClass; + +/* Forward definitions */ +typedef struct _GMtpVolume GMtpVolume; + +struct _GMtpVolumeMonitorClass { + GVolumeMonitorClass parent_class; +}; + +GType g_mtp_volume_monitor_get_type (void) G_GNUC_CONST; + +GVolumeMonitor *g_mtp_volume_monitor_new (void); +void g_mtp_volume_monitor_force_update (GMtpVolumeMonitor *monitor); + +G_END_DECLS + +#endif /* __G_MTP_VOLUME_MONITOR_H__ */ diff --git a/monitor/mtp/mtp-volume-monitor-daemon.c b/monitor/mtp/mtp-volume-monitor-daemon.c new file mode 100644 index 00000000..0b61a591 --- /dev/null +++ b/monitor/mtp/mtp-volume-monitor-daemon.c @@ -0,0 +1,41 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ +/* gvfs - extensions for gio + * + * Copyright (C) 2012 Philip Langdale <philipl@overt.org> + * + * 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 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., 59 Temple Place, Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include <config.h> + +#include <glib.h> +#include <glib/gi18n-lib.h> +#include <gmodule.h> +#include <gio/gio.h> + +#include <gvfsproxyvolumemonitordaemon.h> + +#include "gmtpvolumemonitor.h" + +int +main (int argc, char *argv[]) +{ + g_vfs_proxy_volume_monitor_daemon_init (); + return g_vfs_proxy_volume_monitor_daemon_main (argc, + argv, + "org.gtk.Private.MTPVolumeMonitor", + G_TYPE_MTP_VOLUME_MONITOR); +} diff --git a/monitor/mtp/mtp.monitor b/monitor/mtp/mtp.monitor new file mode 100644 index 00000000..bfb0c7fe --- /dev/null +++ b/monitor/mtp/mtp.monitor @@ -0,0 +1,4 @@ +[RemoteVolumeMonitor] +Name=GProxyVolumeMonitorMTP +DBusName=org.gtk.Private.MTPVolumeMonitor +IsNative=false diff --git a/monitor/mtp/org.gtk.Private.MTPVolumeMonitor.service.in b/monitor/mtp/org.gtk.Private.MTPVolumeMonitor.service.in new file mode 100644 index 00000000..4cd7d190 --- /dev/null +++ b/monitor/mtp/org.gtk.Private.MTPVolumeMonitor.service.in @@ -0,0 +1,3 @@ +[D-BUS Service] +Name=org.gtk.Private.MTPVolumeMonitor +Exec=@libexecdir@/gvfs-mtp-volume-monitor |