ALT Linux Bugzilla
– Attachment 5637 Details for
Bug 27989
Добавить поддержку mtp
New bug
|
Search
|
[?]
|
Help
Register
|
Log In
[x]
|
Forgot Password
Login:
[x]
|
EN
|
RU
[patch]
Одним патчем к сизифному gvfs
gvfs-add-mtp.patch (text/plain), 82.77 KB, created by
Anton V. Boyarshinov
on 2012-11-16 18:53:52 MSK
(
hide
)
Description:
Одним патчем к сизифному gvfs
Filename:
MIME Type:
Creator:
Anton V. Boyarshinov
Created:
2012-11-16 18:53:52 MSK
Size:
82.77 KB
patch
obsolete
>diff --git a/client/gdaemonfile.c b/client/gdaemonfile.c >index 144104b..a8bcd41 100644 >--- a/client/gdaemonfile.c >+++ b/client/gdaemonfile.c >@@ -2807,6 +2807,9 @@ retry: > if (proxy == NULL) > goto out; > >+ /* File transfers can take arbitrarily long amounts of time. */ >+ g_dbus_proxy_set_default_timeout (G_DBUS_PROXY (proxy), G_MAXINT); >+ > data.progress_callback = progress_callback; > data.progress_callback_data = progress_callback_data; > data.context = g_main_context_new (); >diff --git a/common/Makefile.am b/common/Makefile.am >index 07ac8f4..a0d6072 100644 >--- a/common/Makefile.am >+++ b/common/Makefile.am >@@ -1,6 +1,8 @@ > NULL = > >-lib_LTLIBRARIES=libgvfscommon.la >+privlibdir=$(libdir)/gvfs >+privlib_LTLIBRARIES=libgvfscommon.la >+ > noinst_LTLIBRARIES = libgvfscommon-monitor.la > > INCLUDES = -I$(top_srcdir) -I$(top_srcdir)/gvfs \ >@@ -31,7 +33,7 @@ libgvfscommon_la_SOURCES = \ > $(NULL) > > # needed by cygwin (see bug #564003) >-libgvfscommon_la_LDFLAGS = -no-undefined >+libgvfscommon_la_LDFLAGS = -no-undefined -avoid-version > > libgvfscommon_la_LIBADD = \ > $(GLIB_LIBS) \ >diff --git a/configure.ac b/configure.ac >index f5d8f2d..4bc9d21 100644 >--- a/configure.ac >+++ b/configure.ac >@@ -493,6 +493,38 @@ AC_SUBST(BLURAY_CFLAGS) > AC_SUBST(BLURAY_LIBS) > AM_CONDITIONAL(HAVE_BLURAY, [test "$msg_bluray" = "yes"]) > >+dnl ************************* >+dnl *** Check for libmtp *** >+dnl ************************* >+AC_ARG_ENABLE(libmtp, AS_HELP_STRING([--disable-libmtp],[build without libmtp support])) >+msg_libmtp=no >+LIBMTP_LIBS= >+LIBMTP_CFLAGS= >+ >+if test "x$enable_libmtp" != "xno" -a "x$msg_gudev" = "xyes"; then >+ PKG_CHECK_EXISTS(libmtp, msg_libmtp=yes) >+ >+ if test "x$msg_libmtp" = "xyes"; then >+ PKG_CHECK_MODULES(LIBMTP, libmtp >= 1.1.0) >+ AC_DEFINE(HAVE_LIBMTP, 1, [Define to 1 if libmtp is available]) >+ >+ save_libs="$LIBS" >+ LIBS="$LIBMTP_LIBS" >+ AC_CHECK_LIB(mtp, LIBMTP_Get_Thumbnail, have_libmtp_get_thumbnail=yes) >+ if test "x$have_libmtp_get_thumbnail" = "xyes"; then >+ AC_DEFINE(HAVE_LIBMTP_GET_THUMBNAIL, 1, [Define to 1 if LIBMTP_Get_Thumbnail is available]) >+ fi >+ >+ AC_CHECK_LIB(mtp, LIBMTP_Read_Event, have_libmtp_read_event=yes) >+ if test "x$have_libmtp_read_event" = "xyes"; then >+ AC_DEFINE(HAVE_LIBMTP_READ_EVENT, 1, [Define to 1 if LIBMTP_Read_Event is available]) >+ fi >+ LIBS="$save_libs" >+ fi >+fi >+ >+AM_CONDITIONAL(USE_LIBMTP, [test "$msg_libmtp" = "yes"]) >+ > dnl ========================================================================== > dnl Samba 3.0 > >@@ -830,6 +862,7 @@ monitor/gdu/Makefile > monitor/udisks2/Makefile > monitor/gphoto2/Makefile > monitor/afc/Makefile >+monitor/mtp/Makefile > programs/Makefile > man/Makefile > test/Makefile >@@ -850,6 +883,7 @@ echo " > FUSE support: $msg_fuse > CDDA support: $msg_cdda > Gphoto2 support: $msg_gphoto2 >+ MTP support: $msg_libmtp > archive support: $msg_archive > AFC support: $msg_afc > AFP support: $msg_afp >diff --git a/daemon/Makefile.am b/daemon/Makefile.am >index 1cb0cba..45b76f5 100644 >--- a/daemon/Makefile.am >+++ b/daemon/Makefile.am >@@ -79,6 +79,12 @@ mount_DATA += gphoto2.mount > libexec_PROGRAMS += gvfsd-gphoto2 > endif > >+mount_in_files += mtp.mount.in >+if USE_LIBMTP >+mount_DATA += mtp.mount >+libexec_PROGRAMS += gvfsd-mtp >+endif >+ > mount_in_files += obexftp.mount.in > if USE_OBEXFTP > mount_DATA += obexftp.mount >@@ -440,6 +446,19 @@ else > gvfsd_gphoto2_LDADD = $(libraries) $(GPHOTO2_LIBS) $(HAL_LIBS) > endif > >+gvfsd_mtp_SOURCES = \ >+ gvfsbackendmtp.c gvfsbackendmtp.h \ >+ daemon-main.c daemon-main.h \ >+ daemon-main-generic.c >+ >+gvfsd_mtp_CPPFLAGS = \ >+ -DBACKEND_HEADER=gvfsbackendmtp.h \ >+ -DDEFAULT_BACKEND_TYPE=mtp \ >+ -DBACKEND_TYPES='"mtp", G_VFS_TYPE_BACKEND_MTP,' \ >+ $(GUDEV_CFLAGS) $(LIBMTP_CFLAGS) >+ >+gvfsd_mtp_LDADD = $(libraries) $(GUDEV_LIBS) $(LIBMTP_LIBS) >+ > gvfsd_http_SOURCES = \ > soup-input-stream.c soup-input-stream.h \ > gvfsbackendhttp.c gvfsbackendhttp.h \ >diff --git a/daemon/gvfsbackendmtp.c b/daemon/gvfsbackendmtp.c >new file mode 100644 >index 0000000..ac0f350 >--- /dev/null >+++ b/daemon/gvfsbackendmtp.c >@@ -0,0 +1,1401 @@ >+/* GIO - GLib Input, Output and Streaming Library >+ * MTP Backend >+ * >+ * 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 <sys/types.h> >+#include <sys/stat.h> >+#include <errno.h> >+#include <unistd.h> >+#include <fcntl.h> >+#include <string.h> >+#include <stdlib.h> >+ >+#include <glib/gstdio.h> >+#include <glib/gi18n.h> >+#include <gio/gio.h> >+ >+#include <libmtp.h> >+ >+#include "gvfsbackendmtp.h" >+#include "gvfsicon.h" >+#include "gvfsjobopenforread.h" >+#include "gvfsjobread.h" >+#include "gvfsjobseekread.h" >+#include "gvfsjobopenforwrite.h" >+#include "gvfsjobwrite.h" >+#include "gvfsjobclosewrite.h" >+#include "gvfsjobseekwrite.h" >+#include "gvfsjobsetdisplayname.h" >+#include "gvfsjobqueryinfo.h" >+#include "gvfsjobdelete.h" >+#include "gvfsjobqueryfsinfo.h" >+#include "gvfsjobqueryattributes.h" >+#include "gvfsjobenumerate.h" >+#include "gvfsdaemonprotocol.h" >+#include "gvfsjobcreatemonitor.h" >+#include "gvfsjobmakedirectory.h" >+#include "gvfsmonitor.h" >+ >+ >+/* ------------------------------------------------------------------------------------------------- */ >+ >+/* showing debug traces */ >+#define DEBUG_SHOW_TRACES 1 >+#define DEBUG_SHOW_ENUMERATE_TRACES 0 >+ >+static void >+DEBUG (const gchar *message, ...) >+{ >+#if DEBUG_SHOW_TRACES >+ va_list args; >+ va_start (args, message); >+ g_vfprintf (stderr, message, args); >+ va_end (args); >+ g_fprintf (stderr, "\n"); >+ fflush (stderr); >+#endif >+} >+ >+static void >+DEBUG_ENUMERATE (const gchar *message, ...) >+{ >+#if DEBUG_SHOW_ENUMERATE_TRACES >+ va_list args; >+ va_start (args, message); >+ g_vfprintf (stderr, message, args); >+ va_end (args); >+ g_fprintf (stderr, "\n"); >+ fflush (stderr); >+#endif >+} >+ >+ >+/************************************************ >+ * Storage constants copied from ptp.h >+ * >+ * ptp.h is treated as a private header by libmtp >+ ************************************************/ >+ >+/* PTP Storage Types */ >+ >+#define PTP_ST_Undefined 0x0000 >+#define PTP_ST_FixedROM 0x0001 >+#define PTP_ST_RemovableROM 0x0002 >+#define PTP_ST_FixedRAM 0x0003 >+#define PTP_ST_RemovableRAM 0x0004 >+ >+ >+/************************************************ >+ * Initialization >+ ************************************************/ >+ >+G_DEFINE_TYPE (GVfsBackendMtp, g_vfs_backend_mtp, G_VFS_TYPE_BACKEND) >+ >+static void >+g_vfs_backend_mtp_init (GVfsBackendMtp *backend) >+{ >+ DEBUG ("(I) g_vfs_backend_mtp_init"); >+ GMountSpec *mount_spec; >+ >+ g_mutex_init (&backend->mutex); >+ g_vfs_backend_set_display_name (G_VFS_BACKEND (backend), "mtp"); >+ g_vfs_backend_set_icon_name (G_VFS_BACKEND (backend), "multimedia-player"); >+ >+ mount_spec = g_mount_spec_new ("mtp"); >+ g_vfs_backend_set_mount_spec (G_VFS_BACKEND (backend), mount_spec); >+ g_mount_spec_unref (mount_spec); >+ >+ backend->monitors = g_hash_table_new (g_direct_hash, g_direct_equal); >+ >+ DEBUG ("(I) g_vfs_backend_mtp_init done."); >+} >+ >+static void >+g_vfs_backend_mtp_finalize (GObject *object) >+{ >+ GVfsBackendMtp *backend; >+ >+ DEBUG ("(I) g_vfs_backend_mtp_finalize"); >+ >+ backend = G_VFS_BACKEND_MTP (object); >+ >+ g_hash_table_unref (backend->monitors); >+ g_mutex_clear (&backend->mutex); >+ >+ if (G_OBJECT_CLASS (g_vfs_backend_mtp_parent_class)->finalize) >+ (*G_OBJECT_CLASS (g_vfs_backend_mtp_parent_class)->finalize) (object); >+ >+ DEBUG ("(I) g_vfs_backend_mtp_finalize done."); >+} >+ >+ >+/************************************************ >+ * Monitors >+ ************************************************/ >+ >+static void >+do_create_dir_monitor (GVfsBackend *backend, >+ GVfsJobCreateMonitor *job, >+ const char *filename, >+ GFileMonitorFlags flags) >+{ >+ GVfsBackendMtp *mtp_backend = G_VFS_BACKEND_MTP (backend); >+ >+ DEBUG ("(I) create_dir_monitor (%s)", filename); >+ >+ GVfsMonitor *vfs_monitor = g_vfs_monitor_new (backend); >+ >+ g_object_set_data_full (G_OBJECT (vfs_monitor), "gvfsbackendmtp:path", >+ g_strdup (filename), g_free); >+ >+ g_vfs_job_create_monitor_set_monitor (job, vfs_monitor); >+ g_hash_table_insert (mtp_backend->monitors, vfs_monitor, NULL); >+ g_object_weak_ref (G_OBJECT (vfs_monitor), (GWeakNotify)g_hash_table_remove, mtp_backend->monitors); >+ g_object_unref (vfs_monitor); >+ g_vfs_job_succeeded (G_VFS_JOB (job)); >+ >+ DEBUG ("(I) create_dir_monitor done."); >+} >+ >+ >+static void >+do_create_file_monitor (GVfsBackend *backend, >+ GVfsJobCreateMonitor *job, >+ const char *filename, >+ GFileMonitorFlags flags) >+{ >+ GVfsBackendMtp *mtp_backend = G_VFS_BACKEND_MTP (backend); >+ >+ DEBUG ("(I) create_file_monitor (%s)", filename); >+ >+ GVfsMonitor *vfs_monitor = g_vfs_monitor_new (backend); >+ >+ g_object_set_data_full (G_OBJECT (vfs_monitor), "gvfsbackendmtp:path", >+ g_strdup (filename), g_free); >+ >+ g_vfs_job_create_monitor_set_monitor (job, vfs_monitor); >+ g_hash_table_insert (mtp_backend->monitors, vfs_monitor, NULL); >+ g_object_weak_ref (G_OBJECT (vfs_monitor), (GWeakNotify)g_hash_table_remove, mtp_backend->monitors); >+ g_object_unref (vfs_monitor); >+ g_vfs_job_succeeded (G_VFS_JOB (job)); >+ >+ DEBUG ("(I) create_file_monitor done."); >+} >+ >+static void >+emit_event_internal (GVfsMonitor *monitor, >+ const char *path, >+ GFileMonitorEvent event) >+{ >+ DEBUG ("(III) emit_event_internal (%s, %d)", path, event); >+ >+ char *dir = g_dirname (path); >+ const char *monitored_path = g_object_get_data (G_OBJECT (monitor), "gvfsbackendmtp:path"); >+ if (g_strcmp0 (dir, monitored_path) == 0) { >+ DEBUG ("(III) emit_event_internal: Event %d on directory %s for %s", event, dir, path); >+ g_vfs_monitor_emit_event (monitor, event, path, NULL); >+ } else if (g_strcmp0 (path, monitored_path) == 0) { >+ DEBUG ("(III) emit_event_internal: Event %d on file %s", event, path); >+ g_vfs_monitor_emit_event (monitor, event, path, NULL); >+ } >+ g_free (dir); >+ >+ DEBUG ("(III) emit_event_internal done."); >+} >+ >+static void >+emit_create_event (gpointer key, >+ gpointer value, >+ gpointer user_data) >+{ >+ DEBUG ("(II) emit_create_event."); >+ emit_event_internal (key, user_data, G_FILE_MONITOR_EVENT_CREATED); >+} >+ >+static void >+emit_delete_event (gpointer key, >+ gpointer value, >+ gpointer user_data) >+{ >+ DEBUG ("(II) emit_delete_event."); >+ emit_event_internal (key, user_data, G_FILE_MONITOR_EVENT_DELETED); >+} >+ >+static void >+emit_change_event (gpointer key, >+ gpointer value, >+ gpointer user_data) >+{ >+ DEBUG ("(II) emit_change_event."); >+ emit_event_internal (key, user_data, G_FILE_MONITOR_EVENT_CHANGED); >+} >+ >+ >+/************************************************ >+ * Errors >+ ************************************************/ >+ >+static void >+fail_job (GVfsJob *job, LIBMTP_mtpdevice_t *device) >+{ >+ LIBMTP_error_t *error = LIBMTP_Get_Errorstack (device); >+ >+ g_vfs_job_failed (job, G_IO_ERROR, >+ g_vfs_job_is_cancelled (job) ? >+ G_IO_ERROR_CANCELLED : >+ G_IO_ERROR_FAILED, >+ _("libmtp error: %s"), >+ g_strrstr (error->error_text, ":") + 1); >+ >+ LIBMTP_Clear_Errorstack (device); >+} >+ >+ >+/************************************************ >+ * Mounts >+ ************************************************/ >+ >+static LIBMTP_mtpdevice_t * >+get_device (GVfsBackend *backend, const char *id, GVfsJob *job); >+ >+ >+static void >+on_uevent (GUdevClient *client, gchar *action, GUdevDevice *device, gpointer user_data) >+{ >+ const char *dev_path = g_udev_device_get_device_file (device); >+ DEBUG ("(I) on_uevent (action %s, device %s)", action, dev_path); >+ >+ if (dev_path == NULL) { >+ return; >+ } >+ >+ GVfsBackendMtp *op_backend = G_VFS_BACKEND_MTP (user_data); >+ >+ if (g_strcmp0 (op_backend->dev_path, dev_path) == 0 && >+ g_str_equal (action, "remove")) { >+ DEBUG ("(I) on_uevent: Quiting after remove event on device %s", dev_path); >+ /* TODO: need a cleaner way to force unmount ourselves */ >+ exit (1); >+ } >+ >+ DEBUG ("(I) on_uevent done."); >+} >+ >+#if HAVE_LIBMTP_READ_EVENT >+static gpointer >+check_event (gpointer user_data) >+{ >+ GVfsBackendMtp *backend = user_data; >+ >+ LIBMTP_event_t event; >+ int ret = 0; >+ while (ret == 0) { >+ uint32_t param1; >+ char *path; >+ ret = LIBMTP_Read_Event (backend->device, &event, ¶m1); >+ switch (event) { >+ case LIBMTP_EVENT_STORE_ADDED: >+ path = g_strdup_printf ("/%u", param1); >+ g_mutex_lock (&backend->mutex); >+ g_hash_table_foreach (backend->monitors, emit_create_event, path); >+ g_mutex_unlock (&backend->mutex); >+ g_free (path); >+ break; >+ default: >+ break; >+ } >+ } >+ return NULL; >+} >+#endif >+ >+static gboolean >+mtp_heartbeat (GVfsBackendMtp *backend) >+{ >+ if (g_mutex_trylock (&backend->mutex)) { >+ LIBMTP_Dump_Device_Info(backend->device); >+ g_mutex_unlock (&backend->mutex); >+ } >+ return TRUE; >+} >+ >+static void >+do_mount (GVfsBackend *backend, >+ GVfsJobMount *job, >+ GMountSpec *mount_spec, >+ GMountSource *mount_source, >+ gboolean is_automount) >+{ >+ GVfsBackendMtp *op_backend = G_VFS_BACKEND_MTP (backend); >+ >+ DEBUG ("(I) do_mount"); >+ >+ const char *host = g_mount_spec_get (mount_spec, "host"); >+ DEBUG ("(I) do_mount: host=%s", host); >+ if (host == NULL) { >+ GError *error; >+ g_set_error_literal (&error, G_IO_ERROR, G_IO_ERROR_FAILED, _("No device specified")); >+ g_vfs_job_failed_from_error (G_VFS_JOB (job), error); >+ g_error_free (error); >+ return; >+ } >+ >+ const char *subsystems[] = {"usb", NULL}; >+ op_backend->gudev_client = g_udev_client_new (subsystems); >+ if (op_backend->gudev_client == NULL) { >+ GError *error; >+ 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 (op_backend->gudev_client, "uevent", G_CALLBACK (on_uevent), op_backend); >+ >+ /* turn usb:001,041 string into an udev device name */ >+ if (!g_str_has_prefix (host, "[usb:")) { >+ g_vfs_job_failed (G_VFS_JOB (job), >+ G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, >+ _("Unexpected host uri format.")); >+ return; >+ } >+ >+ char *comma; >+ char *dev_path = g_strconcat ("/dev/bus/usb/", host + 5, NULL); >+ if ((comma = strchr (dev_path, ',')) == NULL) { >+ g_free (dev_path); >+ g_vfs_job_failed (G_VFS_JOB (job), >+ G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, >+ _("Malformed host uri.")); >+ return; >+ } >+ *comma = '/'; >+ dev_path[strlen (dev_path) -1] = '\0'; >+ DEBUG ("(I) do_mount: Parsed '%s' into device name %s", host, dev_path); >+ >+ /* find corresponding GUdevDevice */ >+ if (!g_udev_client_query_by_device_file (op_backend->gudev_client, dev_path)) { >+ g_free (dev_path); >+ g_vfs_job_failed (G_VFS_JOB (job), >+ G_IO_ERROR, G_IO_ERROR_NOT_FOUND, >+ _("Couldn't find matching udev device.")); >+ return; >+ } >+ >+ op_backend->dev_path = dev_path; >+ >+ LIBMTP_Init (); >+ >+ get_device (backend, host, G_VFS_JOB (job)); >+ if (!G_VFS_JOB (job)->failed) { >+ GMountSpec *mtp_mount_spec = g_mount_spec_new ("mtp"); >+ g_mount_spec_set (mtp_mount_spec, "host", host); >+ g_vfs_backend_set_mount_spec (backend, mtp_mount_spec); >+ g_mount_spec_unref (mtp_mount_spec); >+ >+ g_vfs_job_succeeded (G_VFS_JOB (job)); >+ >+ op_backend->hb_id = >+ g_timeout_add_seconds (900, (GSourceFunc)mtp_heartbeat, op_backend); >+ >+#if HAVE_LIBMTP_READ_EVENT >+ g_thread_new ("events", check_event, backend); >+#endif >+ } >+ DEBUG ("(I) do_mount done."); >+} >+ >+ >+static void >+do_unmount (GVfsBackend *backend, GVfsJobUnmount *job, >+ GMountUnmountFlags flags, >+ GMountSource *mount_source) >+{ >+ GVfsBackendMtp *op_backend; >+ >+ DEBUG ("(I) do_umount"); >+ >+ op_backend = G_VFS_BACKEND_MTP (backend); >+ >+ g_source_remove (op_backend->hb_id); >+ g_object_unref (op_backend->gudev_client); >+ g_free (op_backend->dev_path); >+ >+ g_mutex_lock (&G_VFS_BACKEND_MTP (backend)->mutex); >+ LIBMTP_Release_Device (op_backend->device); >+ g_mutex_unlock (&G_VFS_BACKEND_MTP (backend)->mutex); >+ >+ g_vfs_job_succeeded (G_VFS_JOB (job)); >+ >+ DEBUG ("(I) do_umount done."); >+} >+ >+ >+ >+ >+ >+ >+ >+/************************************************ >+ * Queries >+ * >+ */ >+ >+LIBMTP_mtpdevice_t * >+get_device (GVfsBackend *backend, const char *id, GVfsJob *job) { >+ DEBUG ("(II) get_device: %s", id); >+ >+ LIBMTP_mtpdevice_t *device = NULL; >+ >+ if (G_VFS_BACKEND_MTP (backend)->device != NULL) { >+ DEBUG ("(II) get_device: Returning cached device %p", device); >+ return G_VFS_BACKEND_MTP (backend)->device; >+ } >+ >+ LIBMTP_raw_device_t * rawdevices; >+ int numrawdevices; >+ LIBMTP_error_number_t err; >+ >+ err = LIBMTP_Detect_Raw_Devices (&rawdevices, &numrawdevices); >+ switch (err) { >+ case LIBMTP_ERROR_NONE: >+ break; >+ case LIBMTP_ERROR_NO_DEVICE_ATTACHED: >+ g_vfs_job_failed (G_VFS_JOB (job), >+ G_IO_ERROR, G_IO_ERROR_NOT_FOUND, >+ _("No MTP devices found")); >+ goto exit; >+ case LIBMTP_ERROR_CONNECTING: >+ g_vfs_job_failed (G_VFS_JOB (job), >+ G_IO_ERROR, G_IO_ERROR_CONNECTION_REFUSED, >+ _("Unable to connect to MTP device")); >+ goto exit; >+ case LIBMTP_ERROR_MEMORY_ALLOCATION: >+ g_vfs_job_failed (G_VFS_JOB (job), >+ G_FILE_ERROR, G_FILE_ERROR_NOMEM, >+ _("Unable to allocate memory while detecting MTP devices")); >+ goto exit; >+ case LIBMTP_ERROR_GENERAL: >+ default: >+ g_vfs_job_failed (G_VFS_JOB (job), >+ G_IO_ERROR, G_IO_ERROR_FAILED, >+ _("Generic libmtp error")); >+ goto exit; >+ } >+ >+ /* Iterate over connected MTP devices */ >+ int i; >+ for (i = 0; i < numrawdevices; i++) { >+ char *name; >+ name = g_strdup_printf ("[usb:%03u,%03u]", >+ rawdevices[i].bus_location, >+ rawdevices[i].devnum); >+ >+ if (strcmp (id, name) == 0) { >+ device = LIBMTP_Open_Raw_Device_Uncached (&rawdevices[i]); >+ if (device == NULL) { >+ g_vfs_job_failed (G_VFS_JOB (job), >+ G_IO_ERROR, G_IO_ERROR_FAILED, >+ _("Unable to open MTP device '%s'"), name); >+ g_free (name); >+ goto exit; >+ } >+ >+ DEBUG ("(II) get_device: Storing device %s", name); >+ G_VFS_BACKEND_MTP (backend)->device = device; >+ >+ LIBMTP_Dump_Errorstack (device); >+ LIBMTP_Clear_Errorstack (device); >+ break; >+ } else { >+ g_free (name); >+ } >+ } >+ >+ exit: >+ DEBUG ("(II) get_device done."); >+ return device; >+} >+ >+static void >+get_device_info (GVfsBackendMtp *backend, GFileInfo *info) >+{ >+ LIBMTP_mtpdevice_t *device = backend->device; >+ const char *name; >+ >+ name = g_mount_spec_get (g_vfs_backend_get_mount_spec (G_VFS_BACKEND (backend)), "host"); >+ >+ DEBUG_ENUMERATE ("(II) get_device_info: %s", name); >+ >+ g_file_info_set_file_type (info, G_FILE_TYPE_DIRECTORY); >+ g_file_info_set_name (info, name); >+ >+ char *friendlyname = LIBMTP_Get_Friendlyname (device); >+ g_file_info_set_display_name (info, friendlyname == NULL ? >+ _("Unnamed Device") : friendlyname); >+ free (friendlyname); >+ >+ g_file_info_set_file_type (info, G_FILE_TYPE_DIRECTORY); >+ g_file_info_set_content_type (info, "inode/directory"); >+ g_file_info_set_size (info, 0); >+ >+ GIcon *icon = g_themed_icon_new ("multimedia-player"); >+ g_file_info_set_icon (info, icon); >+ g_object_unref (icon); >+ >+ g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_ACCESS_CAN_READ, TRUE); >+ g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_ACCESS_CAN_WRITE, TRUE); >+ g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_ACCESS_CAN_DELETE, FALSE); >+ g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_ACCESS_CAN_EXECUTE, TRUE); >+ g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_ACCESS_CAN_TRASH, FALSE); >+ g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_ACCESS_CAN_RENAME, TRUE); >+ >+ g_file_info_set_attribute_string (info, G_FILE_ATTRIBUTE_FILESYSTEM_TYPE, "mtpfs"); >+ >+ int ret = LIBMTP_Get_Storage (device, LIBMTP_STORAGE_SORTBY_NOTSORTED); >+ if (ret != 0) { >+ LIBMTP_Dump_Errorstack (device); >+ LIBMTP_Clear_Errorstack (device); >+ DEBUG_ENUMERATE ("(II) get_device_info done with no stores."); >+ return; >+ } >+ guint64 freeSpace = 0; >+ guint64 maxSpace = 0; >+ LIBMTP_devicestorage_t *storage; >+ for (storage = device->storage; storage != 0; storage = storage->next) { >+ freeSpace += storage->FreeSpaceInBytes; >+ maxSpace += storage->MaxCapacity; >+ } >+ >+ g_file_info_set_attribute_uint64 (info, G_FILE_ATTRIBUTE_FILESYSTEM_FREE, freeSpace); >+ g_file_info_set_attribute_uint64 (info, G_FILE_ATTRIBUTE_FILESYSTEM_SIZE, maxSpace); >+ >+ DEBUG_ENUMERATE ("(II) get_device_info done."); >+} >+ >+static void >+get_storage_info (LIBMTP_devicestorage_t *storage, GFileInfo *info) { >+ >+ char *id = g_strdup_printf ("%u", storage->id); >+ g_file_info_set_name (info, id); >+ g_free (id); >+ >+ DEBUG_ENUMERATE ("(II) get_storage_info: %s", storage->id); >+ >+ g_file_info_set_display_name (info, storage->StorageDescription); >+ g_file_info_set_file_type (info, G_FILE_TYPE_DIRECTORY); >+ g_file_info_set_content_type (info, "inode/directory"); >+ g_file_info_set_size (info, 0); >+ >+ GIcon *icon; >+ switch (storage->StorageType) { >+ case PTP_ST_FixedROM: >+ g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_FILESYSTEM_READONLY, TRUE); >+ icon = g_themed_icon_new_with_default_fallbacks ("drive-harddisk"); >+ break; >+ case PTP_ST_RemovableROM: >+ g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_FILESYSTEM_READONLY, TRUE); >+ icon = g_themed_icon_new_with_default_fallbacks ("media-memory-sd"); >+ break; >+ case PTP_ST_RemovableRAM: >+ g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_FILESYSTEM_READONLY, FALSE); >+ icon = g_themed_icon_new_with_default_fallbacks ("media-memory-sd"); >+ break; >+ case PTP_ST_FixedRAM: >+ default: >+ g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_FILESYSTEM_READONLY, FALSE); >+ icon = g_themed_icon_new_with_default_fallbacks ("drive-harddisk"); >+ break; >+ } >+ g_file_info_set_icon (info, icon); >+ g_object_unref (icon); >+ >+ g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_ACCESS_CAN_READ, TRUE); >+ g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_ACCESS_CAN_WRITE, TRUE); >+ g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_ACCESS_CAN_DELETE, FALSE); >+ g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_ACCESS_CAN_EXECUTE, TRUE); >+ g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_ACCESS_CAN_TRASH, FALSE); >+ g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_ACCESS_CAN_RENAME, FALSE); >+ >+ g_file_info_set_attribute_uint64 (info, G_FILE_ATTRIBUTE_FILESYSTEM_FREE, storage->FreeSpaceInBytes); >+ g_file_info_set_attribute_uint64 (info, G_FILE_ATTRIBUTE_FILESYSTEM_SIZE, storage->MaxCapacity); >+ g_file_info_set_attribute_string (info, G_FILE_ATTRIBUTE_FILESYSTEM_TYPE, "mtpfs"); >+ >+ DEBUG_ENUMERATE ("(II) get_storage_info done."); >+} >+ >+static void >+get_file_info (GVfsBackend *backend, >+ LIBMTP_mtpdevice_t *device, >+ GFileInfo *info, >+ LIBMTP_file_t *file) { >+ GIcon *icon = NULL; >+ char *content_type = NULL; >+ >+ char *id = g_strdup_printf ("%u", file->item_id); >+ g_file_info_set_name (info, id); >+ g_free (id); >+ >+ DEBUG_ENUMERATE ("(II) get_file_info: %u", file->item_id); >+ >+ g_file_info_set_display_name (info, file->filename); >+ >+ switch (file->filetype) { >+ case LIBMTP_FILETYPE_FOLDER: >+ g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_ACCESS_CAN_EXECUTE, TRUE); >+ g_file_info_set_file_type (info, G_FILE_TYPE_DIRECTORY); >+ g_file_info_set_content_type (info, "inode/directory"); >+ icon = g_themed_icon_new ("folder"); >+ break; >+ default: >+ g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_ACCESS_CAN_EXECUTE, FALSE); >+ g_file_info_set_file_type (info, G_FILE_TYPE_REGULAR); >+ content_type = g_content_type_guess (file->filename, NULL, 0, NULL); >+ g_file_info_set_content_type (info, content_type); >+ icon = g_content_type_get_icon (content_type); >+ break; >+ } >+ >+ >+#if HAVE_LIBMTP_GET_THUMBNAIL >+ if (LIBMTP_FILETYPE_IS_IMAGE (file->filetype) || >+ LIBMTP_FILETYPE_IS_VIDEO (file->filetype) || >+ LIBMTP_FILETYPE_IS_AUDIOVIDEO (file->filetype)) { >+ >+ char *icon_id; >+ GIcon *icon; >+ GMountSpec *mount_spec; >+ >+ mount_spec = g_vfs_backend_get_mount_spec (backend); >+ icon_id = g_strdup_printf ("%u", file->item_id); >+ icon = g_vfs_icon_new (mount_spec, >+ icon_id); >+ g_file_info_set_attribute_object (info, >+ G_FILE_ATTRIBUTE_PREVIEW_ICON, >+ G_OBJECT (icon)); >+ g_object_unref (icon); >+ g_free (icon_id); >+ } >+#endif >+ >+ g_file_info_set_size (info, file->filesize); >+ >+ GTimeVal modtime = { file->modificationdate, 0 }; >+ g_file_info_set_modification_time (info, &modtime); >+ >+ g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_ACCESS_CAN_READ, TRUE); >+ g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_ACCESS_CAN_WRITE, TRUE); >+ g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_ACCESS_CAN_DELETE, TRUE); >+ g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_ACCESS_CAN_TRASH, FALSE); >+ g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_ACCESS_CAN_RENAME, TRUE); >+ g_file_info_set_attribute_string (info, G_FILE_ATTRIBUTE_STANDARD_COPY_NAME, file->filename); >+ >+ >+ if (icon != NULL) { >+ g_file_info_set_icon (info, icon); >+ g_object_unref (icon); >+ } >+ g_free (content_type); >+ >+ DEBUG_ENUMERATE ("(II) get_file_info done."); >+} >+ >+ >+static void >+do_enumerate (GVfsBackend *backend, >+ GVfsJobEnumerate *job, >+ const char *filename, >+ GFileAttributeMatcher *attribute_matcher, >+ GFileQueryInfoFlags flags) >+{ >+ GVfsBackendMtp *op_backend = G_VFS_BACKEND_MTP (backend); >+ GFileInfo *info; >+ >+ gchar **elements = g_strsplit_set (filename, "/", -1); >+ unsigned int ne = 0; >+ for (ne = 0; elements[ne] != NULL; ne++); >+ >+ DEBUG ("(I) do_enumerate (filename = %s, n_elements = %d) ", filename, ne); >+ >+ g_mutex_lock (&G_VFS_BACKEND_MTP (backend)->mutex); >+ >+ LIBMTP_mtpdevice_t *device; >+ device = op_backend->device; >+ >+ if (ne == 2 && elements[1][0] == '\0') { >+ LIBMTP_devicestorage_t *storage; >+ >+ int ret = LIBMTP_Get_Storage (device, LIBMTP_STORAGE_SORTBY_NOTSORTED); >+ if (ret != 0) { >+ LIBMTP_Dump_Errorstack (device); >+ LIBMTP_Clear_Errorstack (device); >+ goto success; >+ } >+ for (storage = device->storage; storage != 0; storage = storage->next) { >+ info = g_file_info_new (); >+ get_storage_info (storage, info); >+ g_vfs_job_enumerate_add_info (job, info); >+ g_object_unref (info); >+ } >+ } else { >+ LIBMTP_file_t *files; >+ >+ int pid = (ne == 2 ? -1 : strtol (elements[ne-1], NULL, 10)); >+ >+ LIBMTP_Clear_Errorstack (device); >+ files = LIBMTP_Get_Files_And_Folders (device, strtol (elements[1], NULL, 10), pid); >+ if (files == NULL && LIBMTP_Get_Errorstack (device) != NULL) { >+ fail_job (G_VFS_JOB (job), device); >+ goto exit; >+ } >+ while (files != NULL) { >+ LIBMTP_file_t *file = files; >+ files = files->next; >+ >+ info = g_file_info_new (); >+ get_file_info (backend, device, info, file); >+ g_vfs_job_enumerate_add_info (job, info); >+ g_object_unref (info); >+ >+ LIBMTP_destroy_file_t (file); >+ } >+ } >+ >+ success: >+ g_vfs_job_enumerate_done (job); >+ g_vfs_job_succeeded (G_VFS_JOB (job)); >+ >+ exit: >+ g_strfreev (elements); >+ g_mutex_unlock (&G_VFS_BACKEND_MTP (backend)->mutex); >+ DEBUG ("(I) do_enumerate done."); >+} >+ >+static void >+do_query_info (GVfsBackend *backend, >+ GVfsJobQueryInfo *job, >+ const char *filename, >+ GFileQueryInfoFlags flags, >+ GFileInfo *info, >+ GFileAttributeMatcher *matcher) >+{ >+ DEBUG ("(I) do_query_info (filename = %s) ", filename); >+ g_mutex_lock (&G_VFS_BACKEND_MTP (backend)->mutex); >+ >+ gchar **elements = g_strsplit_set (filename, "/", -1); >+ unsigned int ne = 0; >+ for (ne = 0; elements[ne] != NULL; ne++); >+ >+ LIBMTP_mtpdevice_t *device; >+ device = G_VFS_BACKEND_MTP (backend)->device; >+ >+ if (ne == 2 && elements[1][0] == '\0') { >+ get_device_info (G_VFS_BACKEND_MTP (backend), info); >+ } else if (ne < 3) { >+ LIBMTP_devicestorage_t *storage; >+ int ret = LIBMTP_Get_Storage (device, LIBMTP_STORAGE_SORTBY_NOTSORTED); >+ if (ret != 0) { >+ LIBMTP_Dump_Errorstack (device); >+ LIBMTP_Clear_Errorstack (device); >+ g_vfs_job_failed (G_VFS_JOB (job), >+ G_IO_ERROR, G_IO_ERROR_NOT_FOUND, >+ _("No storage volumes found")); >+ goto exit; >+ } >+ for (storage = device->storage; storage != 0; storage = storage->next) { >+ if (storage->id == strtol (elements[ne-1], NULL, 10)) { >+ DEBUG ("(III) found storage %u", storage->id); >+ get_storage_info (storage, info); >+ } >+ } >+ } else { >+ LIBMTP_file_t *file = NULL; >+ char *endptr; >+ if (strtol (elements[ne-1], &endptr, 10) == 0 || >+ *endptr != '\0') { >+ DEBUG ("(II) try get files and folders"); >+ int parent_id = -1; >+ if (ne > 3) { >+ parent_id = strtol (elements[ne-2], NULL, 10); >+ } >+ LIBMTP_file_t *i = LIBMTP_Get_Files_And_Folders (device, strtol (elements[1], NULL, 10), >+ parent_id); >+ while (i != NULL) { >+ DEBUG ("(II) backup query (entity = %s, name = %s) ", i->filename, elements[ne-1]); >+ if (strcmp (i->filename, elements[ne-1]) == 0) { >+ file = i; >+ i = i->next; >+ break; >+ } else { >+ LIBMTP_file_t *tmp = i; >+ i = i->next; >+ LIBMTP_destroy_file_t (tmp); >+ } >+ } >+ while (i != NULL) { >+ LIBMTP_file_t *tmp = i; >+ i = i->next; >+ LIBMTP_destroy_file_t (tmp); >+ } >+ } else { >+ file = LIBMTP_Get_Filemetadata (device, strtol (elements[ne-1], NULL, 10)); >+ } >+ >+ if (file != NULL) { >+ get_file_info (backend, device, info, file); >+ LIBMTP_destroy_file_t (file); >+ } else { >+ fail_job (G_VFS_JOB (job), device); >+ goto exit; >+ } >+ } >+ >+ g_vfs_job_succeeded (G_VFS_JOB (job)); >+ >+ exit: >+ g_strfreev (elements); >+ g_mutex_unlock (&G_VFS_BACKEND_MTP (backend)->mutex); >+ DEBUG ("(I) do_query_info done."); >+} >+ >+ >+static void >+do_query_fs_info (GVfsBackend *backend, >+ GVfsJobQueryFsInfo *job, >+ const char *filename, >+ GFileInfo *info, >+ GFileAttributeMatcher *attribute_matcher) >+{ >+ DEBUG ("(I) do_query_fs_info (filename = %s) ", filename); >+ g_mutex_lock (&G_VFS_BACKEND_MTP (backend)->mutex); >+ >+ gchar **elements = g_strsplit_set (filename, "/", -1); >+ unsigned int ne = 0; >+ for (ne = 0; elements[ne] != NULL; ne++); >+ >+ LIBMTP_mtpdevice_t *device; >+ device = G_VFS_BACKEND_MTP (backend)->device; >+ >+ if (ne == 2 && elements[1][0] == '\0') { >+ get_device_info (G_VFS_BACKEND_MTP (backend), info); >+ } else { >+ LIBMTP_devicestorage_t *storage; >+ int ret = LIBMTP_Get_Storage (device, LIBMTP_STORAGE_SORTBY_NOTSORTED); >+ if (ret != 0) { >+ LIBMTP_Dump_Errorstack (device); >+ LIBMTP_Clear_Errorstack (device); >+ g_vfs_job_failed (G_VFS_JOB (job), >+ G_IO_ERROR, G_IO_ERROR_NOT_FOUND, >+ _("No storage volumes found")); >+ goto exit; >+ } >+ for (storage = device->storage; storage != 0; storage = storage->next) { >+ if (storage->id == strtol (elements[1], NULL, 10)) { >+ get_storage_info (storage, info); >+ } >+ } >+ } >+ >+ g_vfs_job_succeeded (G_VFS_JOB (job)); >+ >+ exit: >+ g_strfreev (elements); >+ g_mutex_unlock (&G_VFS_BACKEND_MTP (backend)->mutex); >+ >+ DEBUG ("(I) do_query_fs_info done."); >+} >+ >+ >+/************************************************ >+ * Operations >+ * >+ */ >+ >+typedef struct { >+ GFileProgressCallback progress_callback; >+ gpointer progress_callback_data; >+ GVfsJob *job; >+} MtpProgressData; >+ >+ >+static int mtp_progress (uint64_t const sent, uint64_t const total, >+ MtpProgressData const * const data) >+{ >+ if (data->progress_callback) { >+ data->progress_callback (sent, total, data->progress_callback_data); >+ } >+ return g_vfs_job_is_cancelled (data->job); >+} >+ >+static void >+do_make_directory (GVfsBackend *backend, >+ GVfsJobMakeDirectory *job, >+ const char *filename) >+{ >+ DEBUG ("(I) do_make_directory (filename = %s) ", filename); >+ g_mutex_lock (&G_VFS_BACKEND_MTP (backend)->mutex); >+ >+ gchar **elements = g_strsplit_set (filename, "/", -1); >+ unsigned int ne = 0; >+ for (ne = 0; elements[ne] != NULL; ne++); >+ >+ if (ne < 3) { >+ g_vfs_job_failed (G_VFS_JOB (job), >+ G_IO_ERROR, G_IO_ERROR_FAILED, >+ _("Cannot make directory in this location")); >+ goto exit; >+ } >+ >+ LIBMTP_mtpdevice_t *device; >+ device = G_VFS_BACKEND_MTP (backend)->device; >+ >+ int parent_id = 0; >+ if (ne > 3) { >+ parent_id = strtol (elements[ne-2], NULL, 10); >+ } >+ >+ int ret = LIBMTP_Create_Folder (device, elements[ne-1], parent_id, strtol (elements[1], NULL, 10)); >+ if (ret == 0) { >+ fail_job (G_VFS_JOB (job), device); >+ goto exit; >+ } >+ >+ g_vfs_job_succeeded (G_VFS_JOB (job)); >+ >+ g_hash_table_foreach (G_VFS_BACKEND_MTP (backend)->monitors, emit_create_event, (char *)filename); >+ >+ exit: >+ g_strfreev (elements); >+ g_mutex_unlock (&G_VFS_BACKEND_MTP (backend)->mutex); >+ >+ DEBUG ("(I) do_make_directory done."); >+} >+ >+ >+static void >+do_pull (GVfsBackend *backend, >+ GVfsJobPull *job, >+ const char *source, >+ const char *local_path, >+ GFileCopyFlags flags, >+ gboolean remove_source, >+ GFileProgressCallback progress_callback, >+ gpointer progress_callback_data) >+{ >+ DEBUG ("(I) do_pull (filename = %s, local_path = %s) ", source, local_path); >+ g_mutex_lock (&G_VFS_BACKEND_MTP (backend)->mutex); >+ >+ GFileInfo *info = NULL; >+ gchar **elements = g_strsplit_set (source, "/", -1); >+ unsigned int ne = 0; >+ for (ne = 0; elements[ne] != NULL; ne++); >+ >+ if (ne < 3) { >+ g_vfs_job_failed (G_VFS_JOB (job), >+ G_IO_ERROR, G_IO_ERROR_NOT_REGULAR_FILE, >+ _("Not a regular file")); >+ goto exit; >+ } >+ >+ LIBMTP_mtpdevice_t *device; >+ device = G_VFS_BACKEND_MTP (backend)->device; >+ >+ LIBMTP_file_t *file = LIBMTP_Get_Filemetadata (device, strtol (elements[ne-1], NULL, 10)); >+ if (file == NULL) { >+ g_vfs_job_failed (G_VFS_JOB (job), >+ G_IO_ERROR, G_IO_ERROR_NOT_FOUND, >+ _("File does not exist")); >+ goto exit; >+ } >+ >+ info = g_file_info_new (); >+ get_file_info (backend, device, info, file); >+ LIBMTP_destroy_file_t (file); >+ file = NULL; >+ if (g_file_info_get_file_type (info) == G_FILE_TYPE_DIRECTORY) { >+ GError *error; >+ GFile *file = g_file_new_for_path (local_path); >+ g_assert (file != NULL); >+ if (file) { >+ error = NULL; >+ if (g_file_make_directory (file, G_VFS_JOB (job)->cancellable, &error)) { >+ g_vfs_job_succeeded (G_VFS_JOB (job)); >+ } else if (error->code == G_IO_ERROR_EXISTS) { >+ g_vfs_job_succeeded (G_VFS_JOB (job)); >+ g_error_free (error); >+ } else { >+ g_vfs_job_failed_from_error (G_VFS_JOB (job), error); >+ DEBUG ("(II) pull dir failed: %s", error->message); >+ g_error_free (error); >+ } >+ g_object_unref (file); >+ } >+ } else { >+ MtpProgressData *mtp_progress_data = g_new0 (MtpProgressData, 1); >+ mtp_progress_data->progress_callback = progress_callback; >+ mtp_progress_data->progress_callback_data = progress_callback_data; >+ mtp_progress_data->job = G_VFS_JOB (job); >+ int ret = LIBMTP_Get_File_To_File (device, >+ strtol (elements[ne-1], NULL, 10), >+ local_path, >+ (LIBMTP_progressfunc_t)mtp_progress, >+ mtp_progress_data); >+ g_free (mtp_progress_data); >+ if (ret != 0) { >+ fail_job (G_VFS_JOB (job), device); >+ goto exit; >+ } >+ g_vfs_job_succeeded (G_VFS_JOB (job)); >+ } >+ >+ exit: >+ if (info != NULL) { >+ g_object_unref (info); >+ } >+ g_strfreev (elements); >+ g_mutex_unlock (&G_VFS_BACKEND_MTP (backend)->mutex); >+ >+ DEBUG ("(I) do_pull done."); >+} >+ >+ >+static void >+do_push (GVfsBackend *backend, >+ GVfsJobPush *job, >+ const char *destination, >+ const char *local_path, >+ GFileCopyFlags flags, >+ gboolean remove_source, >+ GFileProgressCallback progress_callback, >+ gpointer progress_callback_data) >+{ >+ DEBUG ("(I) do_push (filename = %s, local_path = %s) ", destination, local_path); >+ g_mutex_lock (&G_VFS_BACKEND_MTP (backend)->mutex); >+ >+ GFile *file = NULL; >+ GFileInfo *info = NULL; >+ gchar **elements = g_strsplit_set (destination, "/", -1); >+ unsigned int ne = 0; >+ for (ne = 0; elements[ne] != NULL; ne++); >+ >+ if (ne < 3) { >+ g_vfs_job_failed (G_VFS_JOB (job), >+ G_IO_ERROR, G_IO_ERROR_NOT_REGULAR_FILE, >+ _("Cannot write to this location")); >+ goto exit; >+ } >+ >+ LIBMTP_mtpdevice_t *device; >+ device = G_VFS_BACKEND_MTP (backend)->device; >+ >+ int parent_id = 0; >+ >+ if (ne > 3) { >+ parent_id = strtol (elements[ne-2], NULL, 10); >+ } >+ >+ file = g_file_new_for_path (local_path); >+ if (!file) { >+ g_vfs_job_failed (G_VFS_JOB (job), >+ G_IO_ERROR, G_IO_ERROR_NOT_FOUND, >+ _("File not found")); >+ goto exit; >+ } >+ >+ if (g_file_query_file_type (file, G_FILE_QUERY_INFO_NONE, >+ G_VFS_JOB (job)->cancellable) == >+ G_FILE_TYPE_DIRECTORY) { >+ /* >+ * It happens to be the case that we can reuse do_make_directory >+ * here. >+ */ >+ return do_make_directory (backend, G_VFS_JOB_MAKE_DIRECTORY (job), >+ elements[ne-1]); >+ } >+ >+ GError *error = NULL; >+ info = g_file_query_info (file, G_FILE_ATTRIBUTE_STANDARD_SIZE, >+ G_FILE_QUERY_INFO_NONE, >+ G_VFS_JOB (job)->cancellable, >+ &error); >+ if (!info) { >+ g_vfs_job_failed_from_error (G_VFS_JOB (job), error); >+ g_error_free (error); >+ goto exit; >+ } >+ >+ LIBMTP_file_t *mtpfile = LIBMTP_new_file_t (); >+ mtpfile->filename = strdup (elements[ne-1]); >+ mtpfile->parent_id = parent_id; >+ mtpfile->storage_id = strtol (elements[1], NULL, 10); >+ mtpfile->filetype = LIBMTP_FILETYPE_UNKNOWN; >+ mtpfile->filesize = g_file_info_get_size (info); >+ >+ MtpProgressData *mtp_progress_data = g_new0 (MtpProgressData, 1); >+ mtp_progress_data->progress_callback = progress_callback; >+ mtp_progress_data->progress_callback_data = progress_callback_data; >+ mtp_progress_data->job = G_VFS_JOB (job); >+ int ret = LIBMTP_Send_File_From_File (device, local_path, mtpfile, >+ (LIBMTP_progressfunc_t)mtp_progress, >+ mtp_progress_data); >+ g_free (mtp_progress_data); >+ LIBMTP_destroy_file_t (mtpfile); >+ if (ret != 0) { >+ fail_job (G_VFS_JOB (job), device); >+ goto exit; >+ } >+ >+ g_vfs_job_succeeded (G_VFS_JOB (job)); >+ >+ g_hash_table_foreach (G_VFS_BACKEND_MTP (backend)->monitors, >+ emit_create_event, >+ (char *)destination); >+ >+ exit: >+ if (file) { >+ g_object_unref (file); >+ } >+ if (info) { >+ g_object_unref (info); >+ } >+ g_strfreev (elements); >+ g_mutex_unlock (&G_VFS_BACKEND_MTP (backend)->mutex); >+ >+ DEBUG ("(I) do_push done."); >+} >+ >+ >+static void >+do_delete (GVfsBackend *backend, >+ GVfsJobDelete *job, >+ const char *filename) >+{ >+ DEBUG ("(I) do_delete (filename = %s) ", filename); >+ g_mutex_lock (&G_VFS_BACKEND_MTP (backend)->mutex); >+ >+ gchar **elements = g_strsplit_set (filename, "/", -1); >+ unsigned int ne = 0; >+ for (ne = 0; elements[ne] != NULL; ne++); >+ >+ if (ne < 3) { >+ g_vfs_job_failed (G_VFS_JOB (job), >+ G_IO_ERROR, G_IO_ERROR_FAILED, >+ _("Cannot delete this entity")); >+ goto exit; >+ } >+ >+ LIBMTP_mtpdevice_t *device; >+ device = G_VFS_BACKEND_MTP (backend)->device; >+ >+ int ret = LIBMTP_Delete_Object (device, strtol (elements[ne-1], NULL, 10)); >+ if (ret != 0) { >+ fail_job (G_VFS_JOB (job), device); >+ goto exit; >+ } >+ g_vfs_job_succeeded (G_VFS_JOB (job)); >+ >+ g_hash_table_foreach (G_VFS_BACKEND_MTP (backend)->monitors, >+ emit_delete_event, >+ (char *)filename); >+ >+ exit: >+ g_strfreev (elements); >+ g_mutex_unlock (&G_VFS_BACKEND_MTP (backend)->mutex); >+ >+ DEBUG ("(I) do_delete done."); >+} >+ >+ >+static void >+do_set_display_name (GVfsBackend *backend, >+ GVfsJobSetDisplayName *job, >+ const char *filename, >+ const char *display_name) >+{ >+ DEBUG ("(I) do_set_display_name '%s' --> '%s' ", filename, display_name); >+ g_mutex_lock (&G_VFS_BACKEND_MTP (backend)->mutex); >+ >+ gchar **elements = g_strsplit_set (filename, "/", -1); >+ unsigned int ne = 0; >+ for (ne = 0; elements[ne] != NULL; ne++); >+ >+ if (ne < 3) { >+ g_vfs_job_failed (G_VFS_JOB (job), >+ G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, >+ _("Can't rename volume")); >+ goto exit; >+ } >+ >+ LIBMTP_mtpdevice_t *device; >+ device = G_VFS_BACKEND_MTP (backend)->device; >+ >+ LIBMTP_file_t *file = LIBMTP_Get_Filemetadata (device, strtol (elements[ne-1], NULL, 10)); >+ int ret = LIBMTP_Set_File_Name (device, file, display_name); >+ if (ret != 0) { >+ fail_job (G_VFS_JOB (job), device); >+ goto exit; >+ } >+ LIBMTP_destroy_file_t (file); >+ file = NULL; >+ g_vfs_job_set_display_name_set_new_path (job, filename); >+ g_vfs_job_succeeded (G_VFS_JOB (job)); >+ >+ g_hash_table_foreach (G_VFS_BACKEND_MTP (backend)->monitors, >+ emit_change_event, >+ (char *)filename); >+ >+ exit: >+ g_strfreev (elements); >+ g_mutex_unlock (&G_VFS_BACKEND_MTP (backend)->mutex); >+ >+ DEBUG ("(I) do_set_display_name done."); >+} >+ >+ >+#if HAVE_LIBMTP_GET_THUMBNAIL >+static void >+do_open_icon_for_read (GVfsBackend *backend, >+ GVfsJobOpenIconForRead *job, >+ const char *icon_id) >+{ >+ DEBUG ("(I) do_open_icon_for_read (%s)", icon_id); >+ g_mutex_lock (&G_VFS_BACKEND_MTP (backend)->mutex); >+ >+ guint id = strtol (icon_id, NULL, 10); >+ >+ if (id > 0) { >+ unsigned char *data; >+ unsigned int size; >+ int ret = LIBMTP_Get_Thumbnail (G_VFS_BACKEND_MTP (backend)->device, id, >+ &data, &size); >+ if (ret == 0) { >+ DEBUG ("File %u has thumbnail: %u", id, size); >+ GByteArray *bytes = g_byte_array_sized_new (size); >+ g_byte_array_append (bytes, data, size); >+ free (data); >+ g_vfs_job_open_for_read_set_can_seek (G_VFS_JOB_OPEN_FOR_READ (job), FALSE); >+ g_vfs_job_open_for_read_set_handle (G_VFS_JOB_OPEN_FOR_READ (job), bytes); >+ g_vfs_job_succeeded (G_VFS_JOB (job)); >+ } else { >+ LIBMTP_filesampledata_t *sample_data = LIBMTP_new_filesampledata_t (); >+ ret = LIBMTP_Get_Representative_Sample (G_VFS_BACKEND_MTP (backend)->device, >+ id, sample_data); >+ if (ret == 0) { >+ DEBUG ("File %u has sampledata: %u", id, size); >+ GByteArray *bytes = g_byte_array_sized_new (sample_data->size); >+ g_byte_array_append (bytes, (const guint8 *)sample_data->data, sample_data->size); >+ LIBMTP_destroy_filesampledata_t (sample_data); >+ g_vfs_job_open_for_read_set_can_seek (G_VFS_JOB_OPEN_FOR_READ (job), FALSE); >+ g_vfs_job_open_for_read_set_handle (G_VFS_JOB_OPEN_FOR_READ (job), bytes); >+ g_vfs_job_succeeded (G_VFS_JOB (job)); >+ } else { >+ DEBUG ("File %u has no thumbnail:", id); >+ g_vfs_job_failed (G_VFS_JOB (job), >+ G_IO_ERROR, >+ G_IO_ERROR_NOT_FOUND, >+ _("No thumbnail for entity '%s'"), >+ icon_id); >+ } >+ } >+ } else { >+ g_vfs_job_failed (G_VFS_JOB (job), >+ G_IO_ERROR, >+ G_IO_ERROR_INVALID_ARGUMENT, >+ _("Malformed icon identifier '%s'"), >+ icon_id); >+ } >+ g_mutex_unlock (&G_VFS_BACKEND_MTP (backend)->mutex); >+ >+ DEBUG ("(I) do_open_icon_for_read done."); >+} >+ >+static gboolean >+try_read (GVfsBackend *backend, >+ GVfsJobRead *job, >+ GVfsBackendHandle handle, >+ char *buffer, >+ gsize bytes_requested) >+{ >+ GByteArray *bytes = handle; >+ >+ DEBUG ("(I) try_read (%u %lu)", bytes->len, bytes_requested); >+ >+ gsize bytes_to_copy = MIN (bytes->len, bytes_requested); >+ if (bytes_to_copy == 0) { >+ goto out; >+ } >+ memcpy (buffer, bytes->data, bytes_to_copy); >+ g_byte_array_remove_range (bytes, 0, bytes_to_copy); >+ >+ out: >+ g_vfs_job_read_set_size (job, bytes_to_copy); >+ g_vfs_job_succeeded (G_VFS_JOB (job)); >+ >+ DEBUG ("(I) try_read done."); >+ return TRUE; >+} >+ >+static void >+do_close_read (GVfsBackend *backend, >+ GVfsJobCloseRead *job, >+ GVfsBackendHandle handle) >+{ >+ DEBUG ("(I) do_close_read"); >+ g_byte_array_unref (handle); >+ g_vfs_job_succeeded (G_VFS_JOB (job)); >+ DEBUG ("(I) do_close_read done."); >+} >+#endif /* HAVE_LIBMTP_GET_THUMBNAIL */ >+ >+ >+/************************************************ >+ * Class init >+ * >+ */ >+ >+ >+static void >+g_vfs_backend_mtp_class_init (GVfsBackendMtpClass *klass) >+{ >+ GObjectClass *gobject_class = G_OBJECT_CLASS (klass); >+ GVfsBackendClass *backend_class = G_VFS_BACKEND_CLASS (klass); >+ >+ gobject_class->finalize = g_vfs_backend_mtp_finalize; >+ >+ backend_class->mount = do_mount; >+ backend_class->unmount = do_unmount; >+ backend_class->query_info = do_query_info; >+ backend_class->enumerate = do_enumerate; >+ backend_class->query_fs_info = do_query_fs_info; >+ backend_class->pull = do_pull; >+ backend_class->push = do_push; >+ backend_class->make_directory = do_make_directory; >+ backend_class->delete = do_delete; >+ backend_class->set_display_name = do_set_display_name; >+ backend_class->create_dir_monitor = do_create_dir_monitor; >+ backend_class->create_file_monitor = do_create_file_monitor; >+#if HAVE_LIBMTP_GET_THUMBNAIL >+ backend_class->open_icon_for_read = do_open_icon_for_read; >+ backend_class->try_read = try_read; >+ backend_class->close_read = do_close_read; >+#endif >+} >diff --git a/daemon/gvfsbackendmtp.h b/daemon/gvfsbackendmtp.h >new file mode 100644 >index 0000000..93c1cb9 >--- /dev/null >+++ b/daemon/gvfsbackendmtp.h >@@ -0,0 +1,69 @@ >+/* GIO - GLib Input, Output and Streaming Library >+ * MTP Backend >+ * >+ * 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. >+ */ >+ >+#ifndef __G_VFS_BACKEND_MTP_H__ >+#define __G_VFS_BACKEND_MTP_H__ >+ >+#include <gvfsbackend.h> >+#include <gmountspec.h> >+#ifdef HAVE_GUDEV >+#include <gudev/gudev.h> >+#endif >+#include <libmtp.h> >+ >+G_BEGIN_DECLS >+ >+#define G_VFS_TYPE_BACKEND_MTP (g_vfs_backend_mtp_get_type ()) >+#define G_VFS_BACKEND_MTP(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), G_VFS_TYPE_BACKEND_MTP, GVfsBackendMtp)) >+#define G_VFS_BACKEND_MTP_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), G_VFS_TYPE_BACKEND_MTP, GVfsBackendMtpClass)) >+#define G_VFS_IS_BACKEND_MTP(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), G_VFS_TYPE_BACKEND_MTP)) >+#define G_VFS_IS_BACKEND_MTP_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), G_VFS_TYPE_BACKEND_MTP)) >+#define G_VFS_BACKEND_MTP_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), G_VFS_TYPE_BACKEND_MTP, GVfsBackendMtpClass)) >+ >+typedef struct _GVfsBackendMtp GVfsBackendMtp; >+typedef struct _GVfsBackendMtpClass GVfsBackendMtpClass; >+ >+struct _GVfsBackendMtp >+{ >+ GVfsBackend parent_instance; >+ >+#ifdef HAVE_GUDEV >+ GUdevClient *gudev_client; >+#endif >+ >+ GMutex mutex; >+ LIBMTP_mtpdevice_t *device; >+ char *dev_path; >+ >+ GHashTable *monitors; >+ guint hb_id; >+}; >+ >+struct _GVfsBackendMtpClass >+{ >+ GVfsBackendClass parent_class; >+}; >+ >+GType g_vfs_backend_mtp_get_type (void) G_GNUC_CONST; >+ >+G_END_DECLS >+ >+#endif /* __G_VFS_BACKEND_MTP_H__ */ >diff --git a/daemon/mtp.mount.in b/daemon/mtp.mount.in >new file mode 100644 >index 0000000..9f728a7 >--- /dev/null >+++ b/daemon/mtp.mount.in >@@ -0,0 +1,4 @@ >+[Mount] >+Type=mtp >+Exec=@libexecdir@/gvfsd-mtp >+AutoMount=false >diff --git a/monitor/Makefile.am b/monitor/Makefile.am >index d903df1..7a4e87f 100644 >--- a/monitor/Makefile.am >+++ b/monitor/Makefile.am >@@ -1,4 +1,4 @@ >-DIST_SUBDIRS = proxy hal gdu gphoto2 afc udisks2 >+DIST_SUBDIRS = proxy hal gdu gphoto2 afc udisks2 mtp > SUBDIRS = proxy > > if USE_HAL >@@ -20,3 +20,7 @@ endif > if USE_AFC > SUBDIRS += afc > endif >+ >+if USE_LIBMTP >+SUBDIRS += mtp >+endif >diff --git a/monitor/gphoto2/ggphoto2volumemonitor.c b/monitor/gphoto2/ggphoto2volumemonitor.c >index 64ef383..24811d4 100644 >--- a/monitor/gphoto2/ggphoto2volumemonitor.c >+++ b/monitor/gphoto2/ggphoto2volumemonitor.c >@@ -201,6 +201,13 @@ gudev_add_camera (GGPhoto2VolumeMonitor *monitor, GUdevDevice *device, gboolean > return; > } > #endif /* HAVE_AFC */ >+#ifdef HAVE_LIBMTP >+ if (g_udev_device_get_property_as_boolean (device, "ID_MTP_DEVICE")) >+ { >+ /* g_debug ("ignoring device, is MTP"); */ >+ return; >+ } >+#endif /* HAVE_LIBMTP */ > > usb_bus_num = g_udev_device_get_property (device, "BUSNUM"); > if (usb_bus_num == NULL) { >diff --git a/monitor/mtp/Makefile.am b/monitor/mtp/Makefile.am >new file mode 100644 >index 0000000..2f585c6 >--- /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 0000000..9f44f84 >--- /dev/null >+++ b/monitor/mtp/gmtpvolume.c >@@ -0,0 +1,433 @@ >+/* GIO - GLib Input, Output and Streaming Library >+ * Volume Monitor for MTP Backend >+ * >+ * 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 <string.h> >+#include <sys/wait.h> >+#include <unistd.h> >+ >+#include <glib.h> >+#include <glib/gi18n-lib.h> >+#include <gio/gio.h> >+ >+#include "gmtpvolume.h" >+ >+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 0000000..a1d2e6b >--- /dev/null >+++ b/monitor/mtp/gmtpvolume.h >@@ -0,0 +1,59 @@ >+/* GIO - GLib Input, Output and Streaming Library >+ * Volume Monitor for MTP Backend >+ * >+ * 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. >+ */ >+ >+#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 0000000..f6d1abb >--- /dev/null >+++ b/monitor/mtp/gmtpvolumemonitor.c >@@ -0,0 +1,329 @@ >+/* GIO - GLib Input, Output and Streaming Library >+ * Volume Monitor for MTP Backend >+ * >+ * 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 <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(vm_lock); >+ >+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 (vm_lock); >+ the_volume_monitor = NULL; >+ G_UNLOCK (vm_lock); >+ >+ 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 (vm_lock); >+ >+ l = g_list_copy (monitor->device_volumes); >+ g_list_foreach (l, (GFunc)g_object_ref, NULL); >+ >+ G_UNLOCK (vm_lock); >+ >+ 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; >+ const char *usb_bus_num, *usb_device_num, *uri; >+ GFile *activation_mount_root; >+ >+ 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_debug ("gudev_add_device: device %s (bus: %i, device: %i)", >+ g_udev_device_get_device_file (device), >+ usb_bus_num, usb_device_num); >+ >+ uri = g_strdup_printf ("mtp://[usb:%s,%s]", usb_bus_num, usb_device_num); >+ 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 (vm_lock); >+ if (the_volume_monitor != NULL) { >+ object = g_object_ref (the_volume_monitor); >+ G_UNLOCK (vm_lock); >+ return object; >+ } >+ G_UNLOCK (vm_lock); >+ >+ /*g_warning ("creating vm singleton");*/ >+ >+ 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 (vm_lock); >+ the_volume_monitor = monitor; >+ G_UNLOCK (vm_lock); >+ >+ 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 0000000..0a36a9b >--- /dev/null >+++ b/monitor/mtp/gmtpvolumemonitor.h >@@ -0,0 +1,53 @@ >+/* GIO - GLib Input, Output and Streaming Library >+ * Volume Monitor for MTP Backend >+ * >+ * 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. >+ */ >+ >+#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 0000000..2019940 >--- /dev/null >+++ b/monitor/mtp/mtp-volume-monitor-daemon.c >@@ -0,0 +1,36 @@ >+/* GIO - GLib Input, Output and Streaming Library >+ * Volume Monitor for MTP Backend >+ * >+ * 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 <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 0000000..bfb0c7f >--- /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 0000000..4cd7d19 >--- /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 >diff --git a/programs/gvfs-ls.c b/programs/gvfs-ls.c >index b15cf91..2c45273 100644 >--- a/programs/gvfs-ls.c >+++ b/programs/gvfs-ls.c >@@ -78,7 +78,7 @@ type_to_string (GFileType type) > static void > show_info (GFileInfo *info) > { >- const char *name, *type; >+ const char *name, *display_name, *type; > goffset size; > char **attributes; > int i; >@@ -91,12 +91,16 @@ show_info (GFileInfo *info) > if (name == NULL) > name = ""; > >+ display_name = g_file_info_get_display_name (info); >+ if (display_name == NULL) >+ display_name = name; >+ > size = g_file_info_get_size (info); > type = type_to_string (g_file_info_get_file_type (info)); > if (show_long) >- g_print ("%s\t%"G_GUINT64_FORMAT"\t(%s)", name, (guint64)size, type); >+ g_print ("%s\t%s\t\t\t%"G_GUINT64_FORMAT"\t(%s)", name, display_name, (guint64)size, type); > else >- g_print ("%s", name); >+ g_print ("%s", display_name); > > first_attr = TRUE; > attributes = g_file_info_list_attributes (info, NULL); >@@ -106,6 +110,7 @@ show_info (GFileInfo *info) > > if (!show_long || > strcmp (attributes[i], G_FILE_ATTRIBUTE_STANDARD_NAME) == 0 || >+ strcmp (attributes[i], G_FILE_ATTRIBUTE_STANDARD_DISPLAY_NAME) == 0 || > strcmp (attributes[i], G_FILE_ATTRIBUTE_STANDARD_SIZE) == 0 || > strcmp (attributes[i], G_FILE_ATTRIBUTE_STANDARD_TYPE) == 0 || > strcmp (attributes[i], G_FILE_ATTRIBUTE_STANDARD_IS_HIDDEN) == 0) >@@ -426,6 +431,7 @@ main (int argc, char *argv[]) > } > > attributes = g_strconcat (G_FILE_ATTRIBUTE_STANDARD_NAME "," >+ G_FILE_ATTRIBUTE_STANDARD_DISPLAY_NAME "," > G_FILE_ATTRIBUTE_STANDARD_TYPE "," > G_FILE_ATTRIBUTE_STANDARD_SIZE "," > G_FILE_ATTRIBUTE_STANDARD_IS_HIDDEN, >diff --git a/gvfs/po/POTFILES.in b/gvfs/po/POTFILES.in >index 07e478e..5e2f567 100644 >--- a/gvfs/po/POTFILES.in >+++ b/gvfs/po/POTFILES.in >@@ -32,6 +32,7 @@ daemon/gvfsbackendftp.c > daemon/gvfsbackendgphoto2.c > daemon/gvfsbackendhttp.c > daemon/gvfsbackendlocaltest.c >+daemon/gvfsbackendmtp.c > daemon/gvfsbackendnetwork.c > daemon/gvfsbackendobexftp.c > daemon/gvfsbackendrecent.c
You cannot view the attachment while viewing its details because your browser does not support IFRAMEs.
View the attachment on a separate page
.
View Attachment As Diff
View Attachment As Raw
Actions:
View
|
Diff
Attachments on
bug 27989
: 5637