Re: gio, gvfs and nautilus fixes
- From: David Zeuthen <david fubar dk>
- To: Alexander Larsson <alexl redhat com>
- Cc: nautilus-list gnome org
- Subject: Re: gio, gvfs and nautilus fixes
- Date: Thu, 21 Feb 2008 02:47:43 -0500
And here are patches!
On Thu, 2008-02-21 at 02:46 -0500, David Zeuthen wrote:
> Hey,
>
> Here's a couple of patches for gio, gvfs and Nautilus. The description
> is in the ChangeLog for each module. Some comments
>
> - I'm unsure my fix for nautilus_file_get_mount() is correct and I'm
> not sure what it's supposed to do. My assumption is that it will
> return a GMount for every file on a GMount. The older version only
> did this for the root folder. My fix is provably not correct; we
> may not have created NautilusFile objects to get to the root. One way
> of testing this is to have some removable media with a DCIM/
> directory in the root; for every folder you go into you should see
> a cluebar. Without this fix you will only see it for the root folder
> of the mount.
>
> - Autorunning was broken; this patch unbreaks it by moving the call
> to inhibit into only selected call sites. One thing we can't fix
> is when you mount from computer:// because it runs in another
> process. Ideas on how to fix this?
>
> - g_file_find_enclosing_mount() for GDaemonFile was broken. We just
> cannot construct a new GMount on the fly as it won't have the
> the reference to a foreign GVolume that adopted it. My attempt
> to fix this involves a global variable; it's probably safe as
> GDaemonVolumeMonitor is a singleton anyway. It's not pretty though.
> But it works.
>
> - Before the feature freeze I commited half of the gphoto2 support
> namely the bits in the volume monitor to create, maintain and
> destroy GVolume for connected cameras. This patch is the second
> half, the actual filesystem code. It's lacking write support
> at the moment but is otherwise in pretty good shape (barring
> crashes in libgphoto2; it doesn't like my Sandisk Sansa MTP
> device that much).
>
> So this part of the patch set falls into a grey area wrt feature
> freeze; technically it's just a bug fix (shipping gvfs without
> it will appear broken as you can see the GVolume objects for
> the cameras.. but you have no filesystem code for it!) but I guess
> asking the release team whether it's OK to commit may be in order.
> Shrug.
>
> - For thumbnailing gvfs mounts you need a libgnomeui patch in
> http://bugzilla.gnome.org/show_bug.cgi?id=517276
> There's a few cosmetic details that needs to be cleaned up but
> by and far it works very well. Bastien says a gstreamer with
> gio support release is imminent - when that happens one should
> be able to preview audio from cdda:// mounts!
>
> More details in the ChangeLog's. Thanks for reviewing this.
>
> David
>
> $ diffstat gio-fixes.patch
> ChangeLog | 39 ++++++++++++
> gcontenttype.c | 36 ++++++++++-
> gfile.c | 162 ++++++++++++++++++++++++++++++++++++++++++++++++++
> gfile.h | 24 ++++++-
> gfileinfo.h | 26 ++++++++
> gio.symbols | 3
> glocalfileinfo.c | 59 ++++++------------
> gthemedicon.c | 22 ++++++
> gthemedicon.h | 1
> gunionvolumemonitor.c | 48 ++++++++------
> gvolumemonitor.c | 2
> gvolumemonitor.h | 2
> 12 files changed, 359 insertions(+), 65 deletions(-)
>
> $ diffstat gvfs-fixes.patch
> ChangeLog | 43 +
> client/gdaemonfile.c | 7
> client/gdaemonvolumemonitor.c | 28
> client/gdaemonvolumemonitor.h | 3
> configure.ac | 38 +
> daemon/Makefile.am | 24
> daemon/gphoto2.mount.in | 4
> daemon/gvfsbackendcdda.c | 168 ++++-
> daemon/gvfsbackendgphoto2.c | 1405 ++++++++++++++++++++++++++++++++++++++++++
> daemon/gvfsbackendgphoto2.h | 51 +
> hal/ghaldrive.c | 47 -
> hal/ghalmount.c | 40 +
> hal/ghalvolume.c | 113 ++-
> hal/ghalvolumemonitor.c | 52 +
> hal/ghalvolumemonitor.h | 5
> hal/hal-pool.c | 6
> 16 files changed, 1970 insertions(+), 64 deletions(-)
>
> $ diffstat nautilus-fixes.patch
> ChangeLog | 48 +++++
> libnautilus-private/nautilus-autorun.c | 16 +
> libnautilus-private/nautilus-directory-async.c | 214 ++++++++++++++++++++++-
> libnautilus-private/nautilus-directory-private.h | 6
> libnautilus-private/nautilus-file-attributes.h | 1
> libnautilus-private/nautilus-file-operations.c | 1
> libnautilus-private/nautilus-file-private.h | 4
> libnautilus-private/nautilus-file.c | 97 +++++++++-
> libnautilus-private/nautilus-file.h | 1
> src/file-manager/fm-directory-view.c | 8
> src/file-manager/fm-icon-view.c | 20 +-
> src/nautilus-places-sidebar.c | 2
> src/nautilus-window-manage-views.c | 2
> 13 files changed, 395 insertions(+), 25 deletions(-)
>
>
Index: src/nautilus-window-manage-views.c
===================================================================
--- src/nautilus-window-manage-views.c (revision 13789)
+++ src/nautilus-window-manage-views.c (working copy)
@@ -1405,7 +1405,7 @@
mount = nautilus_file_get_mount (file);
if (mount != NULL) {
- x_content_types = nautilus_autorun_get_x_content_types_for_mount (mount, FALSE);
+ x_content_types = nautilus_autorun_get_x_content_types_for_mount (mount, TRUE);
if (x_content_types != NULL && x_content_types[0] != NULL) {
nautilus_window_show_x_content_bar (window, mount, x_content_types);
}
Index: src/file-manager/fm-icon-view.c
===================================================================
--- src/file-manager/fm-icon-view.c (revision 13789)
+++ src/file-manager/fm-icon-view.c (working copy)
@@ -1888,7 +1888,10 @@
should_preview_sound (NautilusFile *file)
{
GFile *location;
+ GFilesystemPreviewType use_preview;
+ use_preview = nautilus_file_get_filesystem_use_preview (file);
+
location = nautilus_file_get_location (file);
if (g_file_has_uri_scheme (location, "burn")) {
g_object_unref (location);
@@ -1902,10 +1905,23 @@
}
if (preview_sound_auto_value == NAUTILUS_SPEED_TRADEOFF_ALWAYS) {
+ if (use_preview == G_FILESYSTEM_PREVIEW_TYPE_NEVER) {
+ return FALSE;
+ } else {
+ return TRUE;
+ }
+ }
+
+ if (use_preview == G_FILESYSTEM_PREVIEW_TYPE_NEVER) {
+ /* file system says to never preview anything */
+ return FALSE;
+ } else if (use_preview == G_FILESYSTEM_PREVIEW_TYPE_IF_LOCAL) {
+ /* file system says we should treat file as if it's local */
return TRUE;
+ } else {
+ /* only local files */
+ return nautilus_file_is_local (file);
}
-
- return nautilus_file_is_local (file);
}
static int
Index: src/file-manager/fm-directory-view.c
===================================================================
--- src/file-manager/fm-directory-view.c (revision 13789)
+++ src/file-manager/fm-directory-view.c (working copy)
@@ -7552,7 +7552,9 @@
* well as doing a call when ready), in case external forces
* change the directory's file metadata.
*/
- attributes = NAUTILUS_FILE_ATTRIBUTE_METADATA;
+ attributes =
+ NAUTILUS_FILE_ATTRIBUTE_METADATA |
+ NAUTILUS_FILE_ATTRIBUTE_FILESYSTEM_INFO;
view->details->metadata_for_directory_as_file_pending = TRUE;
view->details->metadata_for_files_in_directory_pending = TRUE;
nautilus_file_call_when_ready
@@ -7568,7 +7570,9 @@
/* If capabilities change, then we need to update the menus
* because of New Folder, and relative emblems.
*/
- attributes = NAUTILUS_FILE_ATTRIBUTE_INFO;
+ attributes =
+ NAUTILUS_FILE_ATTRIBUTE_INFO;
+ NAUTILUS_FILE_ATTRIBUTE_FILESYSTEM_INFO;
nautilus_file_monitor_add (view->details->directory_as_file,
&view->details->directory_as_file,
attributes);
Index: src/nautilus-places-sidebar.c
===================================================================
--- src/nautilus-places-sidebar.c (revision 13789)
+++ src/nautilus-places-sidebar.c (working copy)
@@ -1314,6 +1314,7 @@
GVolume *volume;
gtk_tree_model_get (model, &iter, PLACES_SIDEBAR_COLUMN_VOLUME, &volume, -1);
if (volume != NULL) {
+ nautilus_inhibit_autorun_for_volume (volume);
nautilus_file_operations_mount_volume (NULL, volume);
g_object_unref (volume);
}
@@ -1429,6 +1430,7 @@
-1);
if (volume != NULL) {
+ nautilus_inhibit_autorun_for_volume (volume);
nautilus_file_operations_mount_volume (NULL, volume);
g_object_unref (volume);
}
Index: ChangeLog
===================================================================
--- ChangeLog (revision 13789)
+++ ChangeLog (working copy)
@@ -1,3 +1,51 @@
+2008-02-21 David Zeuthen <davidz redhat com>
+
+ * libnautilus-private/nautilus-autorun.c: (should_autorun_mount):
+ Use g_volume_should_automount() to determine if we should autorun
+ programs on a mount.
+
+ * libnautilus-private/nautilus-directory-async.c:
+ (nautilus_directory_set_up_request),
+ (nautilus_async_destroying_file), (lacks_filesystem_info),
+ (wants_filesystem_info), (request_is_satisfied),
+ (directory_count_start), (mime_list_start),
+ (filesystem_info_cancel), (filesystem_info_stop),
+ (filesystem_info_state_free), (got_filesystem_info),
+ (query_filesystem_info_callback), (filesystem_info_start),
+ (start_or_stop_io), (nautilus_directory_cancel),
+ (cancel_filesystem_info_for_file), (cancel_loading_attributes),
+ (nautilus_directory_cancel_loading_file_attributes):
+ * libnautilus-private/nautilus-directory-private.h:
+ * libnautilus-private/nautilus-file-attributes.h: Add new
+ filesystem info attribute; right now two bits in NautilusFile's
+ private structure are set; the filesystem::use-preview and
+ filesystem:readonly.
+
+ * libnautilus-private/nautilus-file-operations.c:
+ (nautilus_file_operations_mount_volume):
+ * src/nautilus-places-sidebar.c: (open_selected_bookmark),
+ (mount_shortcut_cb): Don't inhibit automount from
+ nautilus_file_operations_mount_volume() because then everything
+ will be inhibited as the mount_added callback use it as
+ well. Rather, move the call to inhibit out to the proper call
+ sites.
+
+ * libnautilus-private/nautilus-file-private.h:
+ * libnautilus-private/nautilus-file.c:
+ (nautilus_file_get_filesystem_use_preview),
+ (nautilus_file_should_show_thumbnail),
+ (get_speed_tradeoff_preference_for_file),
+ (nautilus_file_get_mount):
+ * libnautilus-private/nautilus-file.h:
+ * src/file-manager/fm-directory-view.c: (load_directory):
+ * src/file-manager/fm-icon-view.c: (should_preview_sound):
+ * src/nautilus-window-manage-views.c: (update_for_new_location):
+ Use the new filesystem::use-preview hint to determine if we should
+ preview non-native files (such as gphoto2:// and cdda://
+ mounts). Also try to fix nautilus_file_get_mount() so we always
+ get a GMount - this is needed to display the x-content/* cluebar
+ on every subdirectory of a GMount.
+
2008-02-20 Cosimo Cecchi <cosimoc gnome org>
* libnautilus-private/nautilus-mime-application-chooser.c:
Index: libnautilus-private/nautilus-directory-async.c
===================================================================
--- libnautilus-private/nautilus-directory-async.c (revision 13789)
+++ libnautilus-private/nautilus-directory-async.c (working copy)
@@ -89,6 +89,12 @@
NautilusFile *file;
};
+struct FilesystemInfoState {
+ NautilusDirectory *directory;
+ GCancellable *cancellable;
+ NautilusFile *file;
+};
+
struct DirectoryLoadState {
NautilusDirectory *directory;
GCancellable *cancellable;
@@ -628,6 +634,10 @@
request->mount = TRUE;
request->file_info = TRUE;
}
+
+ if (file_attributes & NAUTILUS_FILE_ATTRIBUTE_FILESYSTEM_INFO) {
+ request->filesystem_info = TRUE;
+ }
}
static void
@@ -1581,6 +1591,12 @@
directory->details->mount_state->file = NULL;
changed = TRUE;
}
+
+ if (directory->details->filesystem_info_state != NULL &&
+ directory->details->filesystem_info_state->file == file) {
+ directory->details->filesystem_info_state->file = NULL;
+ changed = TRUE;
+ }
/* Let the directory take care of the rest. */
if (changed) {
@@ -1645,12 +1661,24 @@
}
static gboolean
+lacks_filesystem_info (NautilusFile *file)
+{
+ return !file->details->filesystem_info_is_up_to_date;
+}
+
+static gboolean
wants_info (const Request *request)
{
return request->file_info;
}
static gboolean
+wants_filesystem_info (const Request *request)
+{
+ return request->filesystem_info;
+}
+
+static gboolean
lacks_deep_count (NautilusFile *file)
{
return file->details->deep_counts_status != NAUTILUS_REQUEST_DONE;
@@ -1798,6 +1826,12 @@
}
}
+ if (request->filesystem_info) {
+ if (has_problem (directory, file, lacks_filesystem_info)) {
+ return FALSE;
+ }
+ }
+
if (request->top_left_text) {
if (has_problem (directory, file, lacks_top_left)) {
return FALSE;
@@ -2606,10 +2640,15 @@
directory->details->count_in_progress = state;
+ location = nautilus_file_get_location (file);
#ifdef DEBUG_LOAD_DIRECTORY
- g_message ("load_directory called to get shallow file count for %s", uri);
+ {
+ char *uri;
+ uri = g_file_get_uri (location);
+ g_message ("load_directory called to get shallow file count for %s", uri);
+ g_free (uri);
+ }
#endif
- location = nautilus_file_get_location (file);
g_file_enumerate_children_async (location,
G_FILE_ATTRIBUTE_STANDARD_NAME ","
@@ -3137,11 +3176,16 @@
directory->details->mime_list_in_progress = state;
+ location = nautilus_file_get_location (file);
#ifdef DEBUG_LOAD_DIRECTORY
- g_message ("load_directory called to get MIME list of %s", uri);
+ {
+ char *uri;
+ uri = g_file_get_uri (location);
+ g_message ("load_directory called to get MIME list of %s", uri);
+ g_free (uri);
+ }
#endif
- location = nautilus_file_get_location (file);
g_file_enumerate_children_async (location,
G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE,
0, /* flags */
@@ -3936,7 +3980,7 @@
g_object_unref (file->details->mount);
file->details->mount = NULL;
}
-
+
file->details->mount_is_up_to_date = TRUE;
if (mount) {
file->details->mount = g_object_ref (mount);
@@ -4080,6 +4124,147 @@
}
static void
+filesystem_info_cancel (NautilusDirectory *directory)
+{
+ if (directory->details->filesystem_info_state != NULL) {
+ g_cancellable_cancel (directory->details->filesystem_info_state->cancellable);
+ directory->details->filesystem_info_state->directory = NULL;
+ directory->details->filesystem_info_state = NULL;
+ async_job_end (directory, "filesystem info");
+ }
+}
+
+static void
+filesystem_info_stop (NautilusDirectory *directory)
+{
+ NautilusFile *file;
+
+ if (directory->details->filesystem_info_state != NULL) {
+ file = directory->details->filesystem_info_state->file;
+
+ if (file != NULL) {
+ g_assert (NAUTILUS_IS_FILE (file));
+ g_assert (file->details->directory == directory);
+ if (is_needy (file,
+ lacks_filesystem_info,
+ wants_filesystem_info)) {
+ return;
+ }
+ }
+
+ /* The filesystem info is not wanted, so stop it. */
+ filesystem_info_cancel (directory);
+ }
+}
+
+static void
+filesystem_info_state_free (FilesystemInfoState *state)
+{
+ g_object_unref (state->cancellable);
+ g_free (state);
+}
+
+static void
+got_filesystem_info (FilesystemInfoState *state, GFileInfo *info)
+{
+ NautilusDirectory *directory;
+ NautilusFile *file;
+
+ /* careful here, info may be NULL */
+
+ directory = nautilus_directory_ref (state->directory);
+
+ state->directory->details->filesystem_info_state = NULL;
+ async_job_end (state->directory, "filesystem info");
+
+ file = nautilus_file_ref (state->file);
+
+ file->details->filesystem_info_is_up_to_date = TRUE;
+ if (info != NULL) {
+ file->details->filesystem_use_preview =
+ g_file_info_get_attribute_uint32 (info, G_FILE_ATTRIBUTE_FILESYSTEM_USE_PREVIEW);
+ file->details->filesystem_readonly =
+ g_file_info_get_attribute_boolean (info, G_FILE_ATTRIBUTE_FILESYSTEM_READONLY);
+ }
+
+ nautilus_directory_async_state_changed (directory);
+ nautilus_file_changed (file);
+
+ nautilus_file_unref (file);
+
+ nautilus_directory_unref (directory);
+
+ filesystem_info_state_free (state);
+}
+
+static void
+query_filesystem_info_callback (GObject *source_object,
+ GAsyncResult *res,
+ gpointer user_data)
+{
+ GFileInfo *info;
+ FilesystemInfoState *state;
+
+ state = user_data;
+ if (state->directory == NULL) {
+ /* Operation was cancelled. Bail out */
+ filesystem_info_state_free (state);
+ return;
+ }
+
+ info = g_file_query_filesystem_info_finish (G_FILE (source_object), res, NULL);
+
+ got_filesystem_info (state, info);
+
+ if (info != NULL) {
+ g_object_unref (info);
+ }
+}
+
+static void
+filesystem_info_start (NautilusDirectory *directory,
+ NautilusFile *file,
+ gboolean *doing_io)
+{
+ GFile *location;
+ FilesystemInfoState *state;
+
+ if (directory->details->filesystem_info_state != NULL) {
+ *doing_io = TRUE;
+ return;
+ }
+
+ if (!is_needy (file,
+ lacks_filesystem_info,
+ wants_filesystem_info)) {
+ return;
+ }
+ *doing_io = TRUE;
+
+ if (!async_job_start (directory, "filesystem info")) {
+ return;
+ }
+
+ state = g_new0 (FilesystemInfoState, 1);
+ state->directory = directory;
+ state->file = file;
+ state->cancellable = g_cancellable_new ();
+
+ location = nautilus_file_get_location (file);
+
+ directory->details->filesystem_info_state = state;
+
+ g_file_query_filesystem_info_async (location,
+ G_FILE_ATTRIBUTE_FILESYSTEM_READONLY ","
+ G_FILE_ATTRIBUTE_FILESYSTEM_USE_PREVIEW,
+ G_PRIORITY_DEFAULT,
+ state->cancellable,
+ query_filesystem_info_callback,
+ state);
+ g_object_unref (location);
+}
+
+static void
extension_info_cancel (NautilusDirectory *directory)
{
if (directory->details->extension_info_in_progress != NULL) {
@@ -4257,6 +4442,7 @@
extension_info_stop (directory);
mount_stop (directory);
thumbnail_stop (directory);
+ filesystem_info_stop (directory);
doing_io = FALSE;
/* Take files that are all done off the queue. */
@@ -4285,6 +4471,7 @@
mime_list_start (directory, file, &doing_io);
top_left_start (directory, file, &doing_io);
thumbnail_start (directory, file, &doing_io);
+ filesystem_info_start (directory, file, &doing_io);
if (doing_io) {
return;
@@ -4356,6 +4543,7 @@
extension_info_cancel (directory);
thumbnail_cancel (directory);
mount_cancel (directory);
+ filesystem_info_cancel (directory);
/* We aren't waiting for anything any more. */
if (waiting_directories != NULL) {
@@ -4435,6 +4623,16 @@
}
static void
+cancel_filesystem_info_for_file (NautilusDirectory *directory,
+ NautilusFile *file)
+{
+ if (directory->details->filesystem_info_state != NULL &&
+ directory->details->filesystem_info_state->file == file) {
+ filesystem_info_cancel (directory);
+ }
+}
+
+static void
cancel_link_info_for_file (NautilusDirectory *directory,
NautilusFile *file)
{
@@ -4469,6 +4667,9 @@
if (request.file_info) {
file_info_cancel (directory);
}
+ if (request.filesystem_info) {
+ filesystem_info_cancel (directory);
+ }
if (request.link_info) {
link_info_cancel (directory);
}
@@ -4518,6 +4719,9 @@
if (request.file_info) {
cancel_file_info_for_file (directory, file);
}
+ if (request.filesystem_info) {
+ cancel_filesystem_info_for_file (directory, file);
+ }
if (request.link_info) {
cancel_link_info_for_file (directory, file);
}
Index: libnautilus-private/nautilus-file-private.h
===================================================================
--- libnautilus-private/nautilus-file-private.h (revision 13789)
+++ libnautilus-private/nautilus-file-private.h (working copy)
@@ -206,6 +206,10 @@
eel_boolean_bit can_mount : 1;
eel_boolean_bit can_unmount : 1;
eel_boolean_bit can_eject : 1;
+
+ eel_boolean_bit filesystem_readonly : 1;
+ eel_boolean_bit filesystem_use_preview : 2; /* GFilesystemPreviewType */
+ eel_boolean_bit filesystem_info_is_up_to_date : 1;
};
typedef struct {
Index: libnautilus-private/nautilus-autorun.c
===================================================================
--- libnautilus-private/nautilus-autorun.c (revision 13789)
+++ libnautilus-private/nautilus-autorun.c (working copy)
@@ -1339,10 +1339,12 @@
ignore_autorun = TRUE;
g_object_set_data (G_OBJECT (enclosing_volume), "nautilus-inhibit-autorun", NULL);
}
- g_object_unref (enclosing_volume);
}
if (ignore_autorun) {
+ if (enclosing_volume != NULL) {
+ g_object_unref (enclosing_volume);
+ }
return FALSE;
}
@@ -1360,11 +1362,15 @@
}
}
- if (!g_file_is_native (root)) {
- /* only do autorun on local files */
- /* TODO: Maybe we should do this on some gvfs mounts? like gphoto: ? */
- ignore_autorun = TRUE;
+ /* only do autorun on local files or files where g_volume_should_automount() returns TRUE */
+ ignore_autorun = TRUE;
+ if (g_file_is_native (root) ||
+ (enclosing_volume != NULL && g_volume_should_automount (enclosing_volume))) {
+ ignore_autorun = FALSE;
}
+ if (enclosing_volume != NULL) {
+ g_object_unref (enclosing_volume);
+ }
g_object_unref (root);
return !ignore_autorun;
Index: libnautilus-private/nautilus-directory-private.h
===================================================================
--- libnautilus-private/nautilus-directory-private.h (revision 13789)
+++ libnautilus-private/nautilus-directory-private.h (working copy)
@@ -44,6 +44,7 @@
typedef struct MimeListState MimeListState;
typedef struct ThumbnailState ThumbnailState;
typedef struct MountState MountState;
+typedef struct FilesystemInfoState FilesystemInfoState;
struct NautilusDirectoryDetails
{
@@ -106,6 +107,8 @@
ThumbnailState *thumbnail_state;
MountState *mount_state;
+
+ FilesystemInfoState *filesystem_info_state;
TopLeftTextReadState *top_left_read_state;
@@ -114,6 +117,8 @@
GList *file_operations_in_progress; /* list of FileOperation * */
GHashTable *hidden_file_hash;
+
+
};
/* A request for information about one or more files. */
@@ -130,6 +135,7 @@
gboolean extension_info;
gboolean thumbnail;
gboolean mount;
+ gboolean filesystem_info;
} Request;
NautilusDirectory *nautilus_directory_get_existing (GFile *location);
Index: libnautilus-private/nautilus-file-attributes.h
===================================================================
--- libnautilus-private/nautilus-file-attributes.h (revision 13789)
+++ libnautilus-private/nautilus-file-attributes.h (working copy)
@@ -41,6 +41,7 @@
NAUTILUS_FILE_ATTRIBUTE_EXTENSION_INFO = 1 << 8,
NAUTILUS_FILE_ATTRIBUTE_THUMBNAIL = 1 << 9,
NAUTILUS_FILE_ATTRIBUTE_MOUNT = 1 << 10,
+ NAUTILUS_FILE_ATTRIBUTE_FILESYSTEM_INFO = 1 << 11,
} NautilusFileAttributes;
#endif /* NAUTILUS_FILE_ATTRIBUTES_H */
Index: libnautilus-private/nautilus-file-operations.c
===================================================================
--- libnautilus-private/nautilus-file-operations.c (revision 13789)
+++ libnautilus-private/nautilus-file-operations.c (working copy)
@@ -2040,7 +2040,6 @@
GMountOperation *mount_op;
mount_op = eel_mount_operation_new (parent_window);
- nautilus_inhibit_autorun_for_volume (volume);
g_volume_mount (volume, 0, mount_op, NULL, volume_mount_cb, mount_op);
}
Index: libnautilus-private/nautilus-file.c
===================================================================
--- libnautilus-private/nautilus-file.c (revision 13789)
+++ libnautilus-private/nautilus-file.c (working copy)
@@ -3199,11 +3199,31 @@
return FALSE;
}
+GFilesystemPreviewType
+nautilus_file_get_filesystem_use_preview (NautilusFile *file)
+{
+ GFilesystemPreviewType use_preview;
+ NautilusFile *parent;
+
+ parent = nautilus_file_get_parent (file);
+ if (parent != NULL) {
+ use_preview = parent->details->filesystem_use_preview;
+ g_object_unref (parent);
+ } else {
+ use_preview = 0;
+ }
+
+ return use_preview;
+}
+
gboolean
nautilus_file_should_show_thumbnail (NautilusFile *file)
{
const char *mime_type;
+ GFilesystemPreviewType use_preview;
+ use_preview = nautilus_file_get_filesystem_use_preview (file);
+
mime_type = eel_ref_str_peek (file->details->mime_type);
if (mime_type == NULL) {
mime_type = "application/octet-stream";
@@ -3213,14 +3233,26 @@
nautilus_file_get_size (file) > (unsigned int)cached_thumbnail_limit) {
return FALSE;
}
-
+
if (show_image_thumbs == NAUTILUS_SPEED_TRADEOFF_ALWAYS) {
- return TRUE;
+ if (use_preview == G_FILESYSTEM_PREVIEW_TYPE_NEVER) {
+ return FALSE;
+ } else {
+ return TRUE;
+ }
} else if (show_image_thumbs == NAUTILUS_SPEED_TRADEOFF_NEVER) {
return FALSE;
} else {
- /* only local files */
- return nautilus_file_is_local (file);
+ if (use_preview == G_FILESYSTEM_PREVIEW_TYPE_NEVER) {
+ /* file system says to never thumbnail anything */
+ return FALSE;
+ } else if (use_preview == G_FILESYSTEM_PREVIEW_TYPE_IF_LOCAL) {
+ /* file system says we should treat file as if it's local */
+ return TRUE;
+ } else {
+ /* only local files */
+ return nautilus_file_is_local (file);
+ }
}
return FALSE;
@@ -3715,10 +3747,18 @@
static gboolean
get_speed_tradeoff_preference_for_file (NautilusFile *file, NautilusSpeedTradeoffValue value)
{
+ GFilesystemPreviewType use_preview;
+
g_return_val_if_fail (NAUTILUS_IS_FILE (file), FALSE);
+
+ use_preview = nautilus_file_get_filesystem_use_preview (file);
if (value == NAUTILUS_SPEED_TRADEOFF_ALWAYS) {
- return TRUE;
+ if (use_preview == G_FILESYSTEM_PREVIEW_TYPE_NEVER) {
+ return FALSE;
+ } else {
+ return TRUE;
+ }
}
if (value == NAUTILUS_SPEED_TRADEOFF_NEVER) {
@@ -3726,7 +3766,17 @@
}
g_assert (value == NAUTILUS_SPEED_TRADEOFF_LOCAL_ONLY);
- return nautilus_file_is_local (file);
+
+ if (use_preview == G_FILESYSTEM_PREVIEW_TYPE_NEVER) {
+ /* file system says to never preview anything */
+ return FALSE;
+ } else if (use_preview == G_FILESYSTEM_PREVIEW_TYPE_IF_LOCAL) {
+ /* file system says we should treat file as if it's local */
+ return TRUE;
+ } else {
+ /* only local files */
+ return nautilus_file_is_local (file);
+ }
}
gboolean
@@ -5683,10 +5733,39 @@
GMount *
nautilus_file_get_mount (NautilusFile *file)
{
- if (file->details->mount) {
- return g_object_ref (file->details->mount);
+ NautilusFile *f, *g;
+ GMount *result;
+
+ /* Try to get the mount all the way up the directory chain.
+ *
+ * Note that a GMount may simply not exist for a given
+ * NautilusFile; this is because we only have GMount objects
+ * for user visible mounts.
+ *
+ * As an example this function will return NULL for the
+ * NautilusFile representing the directory /home/user as both
+ * mounts for / or /home are not considered user-visible
+ * mounts.
+ */
+
+ /* TODO: this is not exactly right; the parent folders all the
+ * way up to the root are not necessarily loaded
+ */
+
+ result = NULL;
+ f = nautilus_file_ref (file);
+ while (f != NULL) {
+ if (f->details->mount != NULL) {
+ result = g_object_ref (f->details->mount);
+ nautilus_file_unref (f);
+ break;
+ }
+ g = nautilus_file_get_parent (f);
+ nautilus_file_unref (f);
+ f = g;
}
- return NULL;
+
+ return result;
}
/**
Index: libnautilus-private/nautilus-file.h
===================================================================
--- libnautilus-private/nautilus-file.h (revision 13789)
+++ libnautilus-private/nautilus-file.h (working copy)
@@ -218,6 +218,7 @@
GFileInfo *attributes,
NautilusFileOperationCallback callback,
gpointer callback_data);
+GFilesystemPreviewType nautilus_file_get_filesystem_use_preview (NautilusFile *file);
/* Permissions. */
gboolean nautilus_file_can_get_permissions (NautilusFile *file);
Index: ChangeLog
===================================================================
--- ChangeLog (revision 1315)
+++ ChangeLog (working copy)
@@ -1,3 +1,46 @@
+2008-02-21 David Zeuthen <davidz redhat com>
+
+ * client/gdaemonfile.c: (g_daemon_file_find_enclosing_mount):
+ * client/gdaemonvolumemonitor.c:
+ (g_daemon_volume_monitor_find_mount_by_mount_info),
+ (g_daemon_volume_monitor_init), (g_daemon_volume_monitor_finalize),
+ (is_supported):
+ * client/gdaemonvolumemonitor.h: Prefer to return a GDaemonMount
+ from an existing GDaemonVolumeMonitor rather than rolling our own
+ for GDaemonFile's g_file_find_enclosing_mount()
+ implementation. This is to ensure that g_mount_get_volume() will
+ work properly with mounts that are adopted by GVolume objects from
+ other volume monitors.
+
+ * configure.ac: Check for libgphoto2
+
+ * daemon/Makefile.am:
+ * daemon/gvfsbackendcdda.c: (release_device),
+ (find_udi_for_device), (_hal_device_removed),
+ (g_vfs_backend_cdda_finalize), (do_mount), (do_unmount),
+ (do_query_fs_info), (g_vfs_backend_cdda_class_init): Use HAL to
+ detect when the disc is removed and then forcibly unmount the
+ mount. Report size of disc. Hint the file manager to preview
+ files.
+
+ * hal/ghaldrive.c: (_drive_get_description), (_drive_get_icon):
+ * hal/ghalmount.c: (do_update_from_hal):
+ * hal/ghalvolume.c: (dupv_and_uniqify), (do_update_from_hal),
+ (do_update_from_hal_for_camera), (update_from_hal),
+ (g_hal_volume_new), (g_hal_volume_should_automount):
+ * hal/ghalvolumemonitor.c: (get_hal_pool), (adopt_orphan_mount),
+ (should_drive_be_ignored), (update_cameras):
+ * hal/ghalvolumemonitor.h:
+ * hal/hal-pool.c: (has_cap_only): Ensure that audio and blank CD's
+ are displayed (#514139). Read info.desktop.[icon|name] properties
+ from hal and use these if found. Use proper icon for audio players
+ and make the gphoto2 detection support it as well. Also check for
+ subsystem when filtering on hal capabilities.
+
+ * daemon/gphoto2.mount.in:
+ * daemon/gvfsbackendgphoto2.c:
+ Land the gphoto2 backend.
+
2008-02-20 Alexander Larsson <alexl redhat com>
* daemon/Makefile.am:
Index: configure.ac
===================================================================
--- configure.ac (revision 1315)
+++ configure.ac (working copy)
@@ -222,6 +222,43 @@
AM_CONDITIONAL(USE_HAL, [test "$msg_hal" = "yes"])
+dnl *************************
+dnl *** Check for gphoto2 ***
+dnl *************************
+AC_ARG_ENABLE(hal, [ --disable-gphoto2 build without gphoto2 support])
+msg_gphoto2=no
+GPHOTO2_LIBS=
+GPHOTO2_CFLAGS=
+
+if test "x$enable_gphoto2" != "xno"; then
+ PKG_CHECK_EXISTS(libgphoto2, msg_gphoto2=yes)
+
+ # Need OS tweaks in hal volume monitor backend
+ case "$host" in
+ *-linux*)
+ use_gphoto2=yes
+ ;;
+ *)
+ use_gphoto2=no
+ ;;
+ esac
+
+ if test "x$msg_gphoto2" == "xyes"; then
+ if test "x$use_gphoto2" == "xyes"; then
+ PKG_CHECK_MODULES(GPHOTO2, gphoto2)
+ AC_DEFINE(HAVE_GPHOTO2, 1, [Define to 1 if gphoto2 is available])
+ else
+ AC_MSG_WARN([Not building with gphoto2 support. Need OS tweaks in hal volume monitor.])
+ msg_gphoto2=no
+ fi
+ fi
+fi
+
+AC_SUBST(GPHOTO2_LIBS)
+AC_SUBST(GPHOTO2_CFLAGS)
+
+AM_CONDITIONAL(USE_GPHOTO2, [test "$msg_gphoto2" = "yes"])
+
dnl *******************************
dnl *** Check for GNOME Keyring ***
dnl *******************************
@@ -386,6 +423,7 @@
Samba support: $msg_samba
FUSE support: $msg_fuse
CDDA support: $msg_cdda
+ Gphoto2 support: $msg_gphoto2
GConf support: $msg_gconf
Use HAL for volume monitor: $msg_hal (with fast init path: $have_hal_fast_init)
GNOME Keyring support: $msg_keyring
Index: daemon/gvfsbackendcdda.c
===================================================================
--- daemon/gvfsbackendcdda.c (revision 1315)
+++ daemon/gvfsbackendcdda.c (working copy)
@@ -34,10 +34,13 @@
#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 <libhal.h>
+#include <dbus/dbus.h>
#include "gvfsbackendcdda.h"
#include "gvfsjobopenforread.h"
@@ -52,9 +55,6 @@
/* TODO:
*
* - GVFS integration
- * - Need to unmount ourselves when the backing media is removed
- * - Need to have some way of making our resulting GDaemonMount object (in the client address space)
- * be associated with the GVolume (probably stems from the HAL volume monitor).. both ways
* - g_vfs_backend_set_display_name() needs to work post mount
*
* - Metadata
@@ -86,6 +86,12 @@
{
GVfsBackend parent_instance;
+ DBusConnection *dbus_connection;
+ LibHalContext *hal_ctx;
+ char *hal_udi;
+
+ guint64 size;
+
char *device_path;
cdrom_drive_t *drive;
int num_open_files;
@@ -94,13 +100,80 @@
G_DEFINE_TYPE (GVfsBackendCdda, g_vfs_backend_cdda, G_VFS_TYPE_BACKEND)
static void
+release_device (GVfsBackendCdda *cdda_backend)
+{
+ g_free (cdda_backend->device_path);
+ cdda_backend->device_path = NULL;
+
+ if (cdda_backend->drive != NULL)
+ {
+ cdio_cddap_close (cdda_backend->drive);
+ cdda_backend->drive = NULL;
+ }
+}
+
+static void
+find_udi_for_device (GVfsBackendCdda *cdda_backend)
+{
+ int num_devices;
+ char **devices;
+ int n;
+
+ cdda_backend->hal_udi = NULL;
+
+ devices = libhal_manager_find_device_string_match (cdda_backend->hal_ctx,
+ "block.device",
+ cdda_backend->device_path,
+ &num_devices,
+ NULL);
+ if (devices != NULL)
+ {
+ for (n = 0; n < num_devices && cdda_backend->hal_udi == NULL; n++)
+ {
+ char *udi = devices[n];
+ LibHalPropertySet *ps;
+
+ ps = libhal_device_get_all_properties (cdda_backend->hal_ctx, udi, NULL);
+ if (ps != NULL)
+ {
+ if (libhal_ps_get_bool (ps, "block.is_volume"))
+ {
+ cdda_backend->hal_udi = g_strdup (udi);
+ cdda_backend->size = libhal_ps_get_uint64 (ps, "volume.size");
+ }
+ }
+
+ libhal_free_property_set (ps);
+ }
+ }
+ libhal_free_string_array (devices);
+
+ /*g_warning ("found udi '%s'", cdda_backend->hal_udi);*/
+}
+
+static void
+_hal_device_removed (LibHalContext *hal_ctx, const char *udi)
+{
+ GVfsBackendCdda *cdda_backend;
+
+ cdda_backend = G_VFS_BACKEND_CDDA (libhal_ctx_get_user_data (hal_ctx));
+
+ if (cdda_backend->hal_udi != NULL && strcmp (udi, cdda_backend->hal_udi) == 0)
+ {
+ /*g_warning ("we have been removed!");*/
+ /* TODO: need a cleaner way to force unmount ourselves */
+ exit (1);
+ }
+}
+
+static void
g_vfs_backend_cdda_finalize (GObject *object)
{
GVfsBackendCdda *cdda_backend = G_VFS_BACKEND_CDDA (object);
//g_warning ("finalizing %p", object);
- g_free (cdda_backend->device_path);
+ release_device (cdda_backend);
if (G_OBJECT_CLASS (g_vfs_backend_cdda_parent_class)->finalize)
(*G_OBJECT_CLASS (g_vfs_backend_cdda_parent_class)->finalize) (object);
@@ -135,9 +208,52 @@
GVfsBackendCdda *cdda_backend = G_VFS_BACKEND_CDDA (backend);
GError *error = NULL;
GMountSpec *cdda_mount_spec;
+ DBusError dbus_error;
//g_warning ("do_mount %p", cdda_backend);
+ /* setup libhal */
+
+ dbus_error_init (&dbus_error);
+ cdda_backend->dbus_connection = dbus_bus_get_private (DBUS_BUS_SYSTEM, &dbus_error);
+ if (dbus_error_is_set (&dbus_error))
+ {
+ release_device (cdda_backend);
+ dbus_error_free (&dbus_error);
+ g_set_error (&error, G_IO_ERROR, G_IO_ERROR_FAILED, _("Cannot connect to the system bus"));
+ g_vfs_job_failed_from_error (G_VFS_JOB (job), error);
+ g_error_free (error);
+ return;
+ }
+
+ cdda_backend->hal_ctx = libhal_ctx_new ();
+ if (cdda_backend->hal_ctx == NULL)
+ {
+ release_device (cdda_backend);
+ g_set_error (&error, G_IO_ERROR, G_IO_ERROR_FAILED, _("Cannot create libhal context"));
+ g_vfs_job_failed_from_error (G_VFS_JOB (job), error);
+ g_error_free (error);
+ return;
+ }
+
+ _g_dbus_connection_integrate_with_main (cdda_backend->dbus_connection);
+ libhal_ctx_set_dbus_connection (cdda_backend->hal_ctx, cdda_backend->dbus_connection);
+
+ if (!libhal_ctx_init (cdda_backend->hal_ctx, &dbus_error))
+ {
+ release_device (cdda_backend);
+ dbus_error_free (&dbus_error);
+ g_set_error (&error, G_IO_ERROR, G_IO_ERROR_FAILED, _("Cannot initialize libhal"));
+ g_vfs_job_failed_from_error (G_VFS_JOB (job), error);
+ g_error_free (error);
+ return;
+ }
+
+ libhal_ctx_set_device_removed (cdda_backend->hal_ctx, _hal_device_removed);
+ libhal_ctx_set_user_data (cdda_backend->hal_ctx, cdda_backend);
+
+ /* setup libcdio */
+
host = g_mount_spec_get (mount_spec, "host");
//g_warning ("host=%s", host);
if (host == NULL)
@@ -145,11 +261,14 @@
g_set_error (&error, G_IO_ERROR, G_IO_ERROR_FAILED, "%s", _("No drive specified"));
g_vfs_job_failed_from_error (G_VFS_JOB (job), error);
g_error_free (error);
+ release_device (cdda_backend);
return;
}
cdda_backend->device_path = g_strdup_printf ("/dev/%s", host);
+ find_udi_for_device (cdda_backend);
+
cdda_backend->drive = cdio_cddap_identify (cdda_backend->device_path, 0, NULL);
if (cdda_backend->drive == NULL)
{
@@ -157,6 +276,7 @@
_("Cannot find drive %s"), cdda_backend->device_path);
g_vfs_job_failed_from_error (G_VFS_JOB (job), error);
g_error_free (error);
+ release_device (cdda_backend);
return;
}
@@ -167,11 +287,12 @@
_("Drive %s does not contain audio files"), cdda_backend->device_path);
g_vfs_job_failed_from_error (G_VFS_JOB (job), error);
g_error_free (error);
+ release_device (cdda_backend);
return;
}
/* Translator: %s is the device the disc is inserted into */
- fuse_name = g_strdup_printf (_("Audio Disc on %s"), host);
+ fuse_name = g_strdup_printf (_("cdda mount on %s"), host);
display_name = g_strdup_printf (_("Audio Disc"));
g_vfs_backend_set_stable_name (backend, fuse_name);
g_vfs_backend_set_display_name (backend, display_name);
@@ -241,14 +362,11 @@
return;
}
- if (cdda_backend->drive != NULL)
- {
- //g_warning ("closed drive %p", backend);
- cdio_cddap_close (cdda_backend->drive);
- }
+ release_device (cdda_backend);
+ g_vfs_job_succeeded (G_VFS_JOB (job));
+
//g_warning ("unmounted %p", backend);
- g_vfs_job_succeeded (G_VFS_JOB (job));
}
/* returns -1 if we couldn't map */
@@ -766,6 +884,33 @@
}
static void
+do_query_fs_info (GVfsBackend *backend,
+ GVfsJobQueryFsInfo *job,
+ const char *filename,
+ GFileInfo *info,
+ GFileAttributeMatcher *attribute_matcher)
+{
+ GVfsBackendCdda *cdda_backend = G_VFS_BACKEND_CDDA (backend);
+
+ g_file_info_set_attribute_string (info, G_FILE_ATTRIBUTE_FILESYSTEM_TYPE, "cdda");
+ g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_FILESYSTEM_READONLY, TRUE);
+ g_file_info_set_attribute_uint32 (info, G_FILE_ATTRIBUTE_FILESYSTEM_USE_PREVIEW, G_FILESYSTEM_PREVIEW_TYPE_IF_LOCAL);
+
+ if (cdda_backend->size > 0)
+ {
+ g_file_info_set_attribute_uint64 (info,
+ G_FILE_ATTRIBUTE_FILESYSTEM_SIZE,
+ cdda_backend->size);
+ }
+ g_file_info_set_attribute_uint64 (info,
+ G_FILE_ATTRIBUTE_FILESYSTEM_FREE,
+ 0);
+
+ g_vfs_job_succeeded (G_VFS_JOB (job));
+}
+
+
+static void
g_vfs_backend_cdda_class_init (GVfsBackendCddaClass *klass)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
@@ -781,5 +926,6 @@
backend_class->seek_on_read = do_seek_on_read;
backend_class->close_read = do_close_read;
backend_class->query_info = do_query_info;
+ backend_class->query_fs_info = do_query_fs_info;
backend_class->enumerate = do_enumerate;
}
Index: daemon/Makefile.am
===================================================================
--- daemon/Makefile.am (revision 1315)
+++ daemon/Makefile.am (working copy)
@@ -57,6 +57,12 @@
libexec_PROGRAMS += gvfsd-cdda
endif
+mount_in_files += gphoto2.mount.in
+if USE_GPHOTO2
+mount_DATA += gphoto2.mount
+libexec_PROGRAMS += gvfsd-gphoto2
+endif
+
mount_in_files += network.mount.in
if USE_GCONF
mount_DATA += network.mount
@@ -262,11 +268,25 @@
-DBACKEND_HEADER=gvfsbackendcdda.h \
-DDEFAULT_BACKEND_TYPE=cdda \
-DMAX_JOB_THREADS=1 \
- $(CDDA_CFLAGS) \
+ $(CDDA_CFLAGS) $(HAL_CFLAGS) \
-DBACKEND_TYPES='"cdda", G_VFS_TYPE_BACKEND_CDDA,'
-gvfsd_cdda_LDADD = $(libraries) $(CDDA_LIBS)
+gvfsd_cdda_LDADD = $(libraries) $(CDDA_LIBS) $(HAL_LIBS)
+gvfsd_gphoto2_SOURCES = \
+ gvfsbackendgphoto2.c gvfsbackendgphoto2.h \
+ daemon-main.c daemon-main.h \
+ daemon-main-generic.c
+
+gvfsd_gphoto2_CPPFLAGS = \
+ -DBACKEND_HEADER=gvfsbackendgphoto2.h \
+ -DDEFAULT_BACKEND_TYPE=gphoto2 \
+ -DMAX_JOB_THREADS=1 \
+ $(GPHOTO2_CFLAGS) $(HAL_CFLAGS) \
+ -DBACKEND_TYPES='"gphoto2", G_VFS_TYPE_BACKEND_GPHOTO2,'
+
+gvfsd_gphoto2_LDADD = $(libraries) $(GPHOTO2_LIBS) $(HAL_LIBS)
+
gvfsd_http_SOURCES = \
soup-input-stream.c soup-input-stream.h \
soup-output-stream.c soup-output-stream.h \
Index: hal/ghalmount.c
===================================================================
--- hal/ghalmount.c (revision 1315)
+++ hal/ghalmount.c (working copy)
@@ -366,10 +366,8 @@
{
HalDevice *volume;
HalDevice *drive;
-
char *name;
const char *icon_name;
-
const char *drive_type;
const char *drive_bus;
gboolean drive_uses_removable_media;
@@ -380,6 +378,11 @@
gboolean volume_disc_has_data;
const char *volume_disc_type;
gboolean volume_disc_is_blank;
+ gboolean is_audio_player;
+ const char *icon_from_hal;
+ const char *volume_icon_from_hal;
+ const char *name_from_hal;
+ const char *volume_name_from_hal;
volume = m->device;
drive = m->drive_device;
@@ -394,13 +397,30 @@
volume_disc_has_data = hal_device_get_property_bool (volume, "volume.disc.has_data");
volume_disc_is_blank = hal_device_get_property_bool (volume, "volume.disc.is_blank");
volume_disc_type = hal_device_get_property_string (volume, "volume.disc.type");
-
+ is_audio_player = hal_device_has_capability (drive, "portable_audio_player");
+ icon_from_hal = hal_device_get_property_string (drive, "info.desktop.icon");
+ volume_icon_from_hal = hal_device_get_property_string (volume, "info.desktop.icon");
+ name_from_hal = hal_device_get_property_string (drive, "info.desktop.name");
+ volume_name_from_hal = hal_device_get_property_string (volume, "info.desktop.name");
+
/*g_warning ("drive_type='%s'", drive_type); */
/*g_warning ("drive_bus='%s'", drive_bus); */
/*g_warning ("drive_uses_removable_media=%d", drive_uses_removable_media); */
-
- if (strcmp (drive_type, "disk") == 0)
+
+ if (strlen (volume_icon_from_hal) > 0)
{
+ icon_name = volume_icon_from_hal;
+ }
+ else if (strlen (icon_from_hal) > 0)
+ {
+ icon_name = icon_from_hal;
+ }
+ else if (is_audio_player)
+ {
+ icon_name = "multimedia-player";
+ }
+ else if (strcmp (drive_type, "disk") == 0)
+ {
if (strcmp (drive_bus, "ide") == 0)
icon_name = "drive-harddisk-ata";
else if (strcmp (drive_bus, "scsi") == 0)
@@ -430,7 +450,15 @@
icon_name = "drive-harddisk";
- if (volume_fs_label != NULL && strlen (volume_fs_label) > 0)
+ if (strlen (volume_name_from_hal) > 0)
+ {
+ name = g_strdup (volume_name_from_hal);
+ }
+ else if (strlen (name_from_hal) > 0)
+ {
+ name = g_strdup (name_from_hal);
+ }
+ else if (volume_fs_label != NULL && strlen (volume_fs_label) > 0)
name = g_strdup (volume_fs_label);
else if (volume_is_disc)
{
Index: hal/ghalvolume.c
===================================================================
--- hal/ghalvolume.c (revision 1315)
+++ hal/ghalvolume.c (working copy)
@@ -202,6 +202,36 @@
return str;
}
+static char **
+dupv_and_uniqify (char **str_array)
+{
+ int n, m, o;
+ int len;
+ char **result;
+
+ result = g_strdupv (str_array);
+ len = g_strv_length (result);
+
+ for (n = 0; n < len; n++)
+ {
+ char *s = result[n];
+ for (m = n + 1; m < len; m++)
+ {
+ char *p = result[m];
+ if (strcmp (s, p) == 0)
+ {
+ for (o = m + 1; o < len; o++)
+ result[o - 1] = result[o];
+ len--;
+ result[len] = NULL;
+ m--;
+ }
+ }
+ }
+
+ return result;
+}
+
static void
do_update_from_hal (GHalVolume *mv)
{
@@ -285,7 +315,7 @@
g_object_set_data_full (G_OBJECT (mv),
"hal-storage-device-capabilities",
- g_strdupv (hal_device_get_property_strlist (mv->drive_device, "info.capabilities")),
+ dupv_and_uniqify (hal_device_get_property_strlist (mv->drive_device, "info.capabilities")),
(GDestroyNotify) g_strfreev);
if (volume_disc_type != NULL && strlen (volume_disc_type) == 0)
@@ -296,33 +326,81 @@
(GDestroyNotify) g_free);
}
-#ifdef _WITH_GPHOTO2
+#ifdef HAVE_GPHOTO2
static void
do_update_from_hal_for_camera (GHalVolume *v)
{
const char *vendor;
const char *product;
+ const char *icon_from_hal;
+ const char *name_from_hal;
+ gboolean is_audio_player;
vendor = hal_device_get_property_string (v->drive_device, "usb_device.vendor");
product = hal_device_get_property_string (v->drive_device, "usb_device.product");
+ icon_from_hal = hal_device_get_property_string (v->device, "info.desktop.icon");
+ name_from_hal = hal_device_get_property_string (v->device, "info.desktop.name");
- if (vendor == NULL)
+ is_audio_player = hal_device_has_capability (v->device, "portable_audio_player");
+
+ v->name = NULL;
+ if (strlen (name_from_hal) > 0)
{
+ v->name = g_strdup (name_from_hal);
+ }
+ else if (vendor == NULL)
+ {
if (product != NULL)
v->name = g_strdup (product);
- else
- v->name = g_strdup (_("Camera"));
}
else
{
if (product != NULL)
- v->name = g_strdup_printf ("%s %s", vendor, product);
+ {
+ v->name = g_strdup_printf ("%s %s", vendor, product);
+ }
else
- v->name = g_strdup_printf (_("%s Camera"), vendor);
+ {
+ if (is_audio_player)
+ {
+ v->name = g_strdup_printf (_("%s Audio Player"), vendor);
+ }
+ else
+ {
+ v->name = g_strdup_printf (_("%s Camera"), vendor);
+ }
+ }
}
+ if (v->name == NULL)
+ {
+ if (is_audio_player)
+ {
+ v->name = g_strdup (_("Audio Player"));
+ }
+ else
+ {
+ v->name = g_strdup (_("Camera"));
+ }
+ }
- v->icon = g_strdup ("camera");
+ if (strlen (icon_from_hal) > 0)
+ {
+ v->icon = g_strdup (icon_from_hal);
+ }
+ else if (is_audio_player)
+ {
+ v->icon = g_strdup ("multimedia-player");
+ }
+ else
+ {
+ v->icon = g_strdup ("camera");
+ }
v->mount_path = NULL;
+
+ g_object_set_data_full (G_OBJECT (v),
+ "hal-storage-device-capabilities",
+ dupv_and_uniqify (hal_device_get_property_strlist (v->device, "info.capabilities")),
+ (GDestroyNotify) g_strfreev);
}
#endif
@@ -340,8 +418,10 @@
g_free (mv->name);
g_free (mv->icon);
g_free (mv->mount_path);
-#ifdef _WITH_GPHOTO2
- if (hal_device_has_capability (mv->device, "camera"))
+#ifdef HAVE_GPHOTO2
+ if (hal_device_has_capability (mv->device, "camera") ||
+ (hal_device_has_capability (mv->device, "portable_audio_player") &&
+ hal_device_get_property_bool (mv->device, "camera.libgphoto2.support")))
do_update_from_hal_for_camera (mv);
else
do_update_from_hal (mv);
@@ -432,9 +512,12 @@
device_path = hal_device_get_property_string (device, "block.device");
}
-#ifdef _WITH_GPHOTO2
- else if (hal_device_has_capability (device, "camera"))
+#ifdef HAVE_GPHOTO2
+ else if (hal_device_has_capability (device, "camera") ||
+ (hal_device_has_capability (device, "portable_audio_player") &&
+ hal_device_get_property_bool (device, "camera.libgphoto2.support")))
{
+
/* OK, so we abuse storage_udi and drive_device for the USB main
* device that holds this interface...
*/
@@ -609,10 +692,8 @@
static gboolean
g_hal_volume_should_automount (GVolume *volume)
{
- GHalVolume *hal_volume = G_HAL_VOLUME (volume);
- /* TODO: For now, just never automount things that are not
- local. Need to figure out a better approach later. */
- return hal_volume->foreign_mount == NULL;
+ /* all of the volumes we manage should be automounted */
+ return TRUE;
}
static GDrive *
Index: hal/ghaldrive.c
===================================================================
--- hal/ghaldrive.c (revision 1315)
+++ hal/ghaldrive.c (working copy)
@@ -123,12 +123,19 @@
char *s = NULL;
const char *drive_type;
const char *drive_bus;
+ const char *name_from_hal;
drive_type = hal_device_get_property_string (d, "storage.drive_type");
drive_bus = hal_device_get_property_string (d, "storage.bus");
+ name_from_hal = hal_device_get_property_string (d, "info.desktop.name");
- if (strcmp (drive_type, "cdrom") == 0)
+
+ if (strlen (name_from_hal) > 0)
{
+ s = g_strdup (name_from_hal);
+ }
+ else if (strcmp (drive_type, "cdrom") == 0)
+ {
const char *first;
const char *second;
@@ -230,22 +237,34 @@
char *s = NULL;
const char *drive_type;
const char *drive_bus;
+ const char *icon_from_hal;
+ gboolean is_audio_player;
drive_type = hal_device_get_property_string (d, "storage.drive_type");
drive_bus = hal_device_get_property_string (d, "storage.bus");
-
- if (strcmp (drive_type, "disk") == 0) {
- if (strcmp (drive_bus, "ide") == 0)
- s = g_strdup ("drive-removable-media-ata");
- else if (strcmp (drive_bus, "scsi") == 0)
- s = g_strdup ("drive-removable-media-scsi");
- else if (strcmp (drive_bus, "ieee1394") == 0)
- s = g_strdup ("drive-removable-media-ieee1394");
- else if (strcmp (drive_bus, "usb") == 0)
- s = g_strdup ("drive-removable-media-usb");
- else
- s = g_strdup ("drive-removable-media");
- }
+ is_audio_player = hal_device_has_capability (d, "portable_audio_player");
+ icon_from_hal = hal_device_get_property_string (d, "info.desktop.icon");
+
+ if (strlen (icon_from_hal) > 0)
+ {
+ s = g_strdup (icon_from_hal);
+ }
+ else if (is_audio_player)
+ {
+ s = g_strdup ("multimedia-player");
+ } else if (strcmp (drive_type, "disk") == 0)
+ {
+ if (strcmp (drive_bus, "ide") == 0)
+ s = g_strdup ("drive-removable-media-ata");
+ else if (strcmp (drive_bus, "scsi") == 0)
+ s = g_strdup ("drive-removable-media-scsi");
+ else if (strcmp (drive_bus, "ieee1394") == 0)
+ s = g_strdup ("drive-removable-media-ieee1394");
+ else if (strcmp (drive_bus, "usb") == 0)
+ s = g_strdup ("drive-removable-media-usb");
+ else
+ s = g_strdup ("drive-removable-media");
+ }
else if (strcmp (drive_type, "cdrom") == 0)
{
/* TODO: maybe there's a better heuristic than this */
Index: hal/hal-pool.c
===================================================================
--- hal/hal-pool.c (revision 1315)
+++ hal/hal-pool.c (working copy)
@@ -137,12 +137,18 @@
static gboolean
has_cap_only (HalPool *pool, HalDevice *device)
{
+ const char *subsys;
unsigned int n;
for (n = 0; pool->priv->cap_only != NULL && pool->priv->cap_only[n] != NULL; n++)
{
if (hal_device_has_capability (device, pool->priv->cap_only[n]))
return TRUE;
+
+ subsys = hal_device_get_property_string (device, "info.subsystem");
+
+ if (subsys != NULL && strcmp (subsys, pool->priv->cap_only[n]) == 0)
+ return TRUE;
}
return FALSE;
Index: hal/ghalvolumemonitor.c
===================================================================
--- hal/ghalvolumemonitor.c (revision 1315)
+++ hal/ghalvolumemonitor.c (working copy)
@@ -93,7 +93,7 @@
static HalPool *
get_hal_pool (void)
{
- char *cap_only[] = {"block", "camera", NULL};
+ char *cap_only[] = {"block", "camera", "portable_audio_player", "usb_device", NULL};
if (pool == NULL)
pool = hal_pool_new (cap_only);
@@ -433,7 +433,7 @@
ret = NULL;
mount_root = g_mount_get_root (mount);
- /* right now we only support discs as foreign mounts */
+ /* cdda:// as foreign mounts */
for (l = the_volume_monitor->disc_volumes; l != NULL; l = l->next)
{
GHalVolume *volume = l->data;
@@ -442,9 +442,24 @@
{
g_hal_volume_adopt_foreign_mount (volume, mount);
ret = g_object_ref (volume);
- break;
+ goto found;
}
}
+
+ /* gphoto2:// as foreign mounts */
+ for (l = the_volume_monitor->camera_volumes; l != NULL; l = l->next)
+ {
+ GHalVolume *volume = l->data;
+
+ if (g_hal_volume_has_foreign_mount_root (volume, mount_root))
+ {
+ g_hal_volume_adopt_foreign_mount (volume, mount);
+ ret = g_object_ref (volume);
+ goto found;
+ }
+ }
+
+ found:
g_object_unref (mount_root);
return ret;
@@ -647,7 +662,7 @@
return NULL;
}
-#ifdef _WITH_GPHOTO2
+#ifdef HAVE_GPHOTO2
static GHalVolume *
find_camera_volume_by_udi (GHalVolumeMonitor *monitor, const char *udi)
{
@@ -724,8 +739,12 @@
const char *drive_udi;
gboolean all_volumes_ignored, got_volumes;
+ /* never ignore drives with removable media */
+ if (hal_device_get_property_bool (d, "storage.removable"))
+ return FALSE;
+
drive_udi = hal_device_get_udi (d);
-
+
volumes = hal_pool_find_by_capability (pool, "volume");
all_volumes_ignored = TRUE;
@@ -736,7 +755,9 @@
if (strcmp (drive_udi, hal_device_get_property_string (volume_dev, "block.storage_device")) == 0)
{
got_volumes = TRUE;
- if (!should_volume_be_ignored (pool, volume_dev))
+ if (!should_volume_be_ignored (pool, volume_dev) ||
+ hal_device_get_property_bool (volume_dev, "volume.disc.has_audio") ||
+ hal_device_get_property_bool (volume_dev, "volume.disc.is_blank"))
{
all_volumes_ignored = FALSE;
break;
@@ -1096,14 +1117,29 @@
static void
update_cameras (GHalVolumeMonitor *monitor)
{
-#ifdef _WITH_GPHOTO2
+#ifdef HAVE_GPHOTO2
GList *new_camera_devices;
+ GList *new_mpt_devices;
GList *removed, *added;
GList *l, *ll;
GHalVolume *volume;
const char *udi;
+ new_mpt_devices = hal_pool_find_by_capability (monitor->pool, "portable_audio_player");
+ for (l = new_mpt_devices; l != NULL; l = ll)
+ {
+ HalDevice *d = l->data;
+ ll = l->next;
+ if (! hal_device_get_property_bool (d, "camera.libgphoto2.support"))
+ {
+ /*g_warning ("ignoring %s", hal_device_get_udi (d));*/
+ /* filter out everything that isn't supported by libgphoto2 */
+ new_mpt_devices = g_list_delete_link (new_mpt_devices, l);
+ }
+ }
+
new_camera_devices = hal_pool_find_by_capability (monitor->pool, "camera");
+ new_camera_devices = g_list_concat (new_camera_devices, new_mpt_devices);
for (l = new_camera_devices; l != NULL; l = ll)
{
HalDevice *d = l->data;
@@ -1152,7 +1188,7 @@
usb_bus_num = hal_device_get_property_int (d, "usb.bus_number");
usb_device_num = hal_device_get_property_int (d, "usb.linux.device_number");
- uri = g_strdup_printf ("gphoto2://usb:%03d,%03d", usb_bus_num, usb_device_num);
+ uri = g_strdup_printf ("gphoto2://[usb:%03d,%03d]", usb_bus_num, usb_device_num);
/*g_warning ("uri is '%s'", uri);*/
foreign_mount_root = g_file_new_for_uri (uri);
g_free (uri);
Index: hal/ghalvolumemonitor.h
===================================================================
--- hal/ghalvolumemonitor.h (revision 1315)
+++ hal/ghalvolumemonitor.h (working copy)
@@ -29,11 +29,6 @@
#include <gio/gio.h>
#include <gio/gunixmounts.h>
-/* TODO: need to use different properties on HAL for other OS's (!) */
-#ifdef __linux__
-#define _WITH_GPHOTO2
-#endif
-
G_BEGIN_DECLS
#define G_TYPE_HAL_VOLUME_MONITOR (g_hal_volume_monitor_get_type ())
Index: client/gdaemonfile.c
===================================================================
--- client/gdaemonfile.c (revision 1315)
+++ client/gdaemonfile.c (working copy)
@@ -1530,7 +1530,12 @@
if (mount_info->user_visible)
{
- mount = g_daemon_mount_new (mount_info, NULL);
+ /* if we have a daemon volume monitor then return one of it's mounts */
+ mount = g_daemon_volume_monitor_find_mount_by_mount_info (mount_info);
+ if (mount == NULL)
+ {
+ mount = g_daemon_mount_new (mount_info, NULL);
+ }
g_mount_info_unref (mount_info);
if (mount)
Index: client/gdaemonvolumemonitor.c
===================================================================
--- client/gdaemonvolumemonitor.c (revision 1315)
+++ client/gdaemonvolumemonitor.c (working copy)
@@ -31,6 +31,8 @@
#include "gdaemonvfs.h"
#include "gmounttracker.h"
+static GDaemonVolumeMonitor *_the_daemon_volume_monitor;
+
struct _GDaemonVolumeMonitor {
GVolumeMonitor parent;
@@ -103,6 +105,27 @@
return found_mount;
}
+GDaemonMount *
+g_daemon_volume_monitor_find_mount_by_mount_info (GMountInfo *mount_info)
+{
+ GDaemonMount *daemon_mount;
+
+ if (_the_daemon_volume_monitor == NULL)
+ {
+ return NULL;
+ }
+
+ daemon_mount = find_mount_by_mount_info (_the_daemon_volume_monitor, mount_info);
+ if (daemon_mount != NULL)
+ {
+ return g_object_ref (daemon_mount);
+ }
+ else
+ {
+ return NULL;
+ }
+}
+
static void
mount_added (GDaemonVolumeMonitor *daemon_monitor, GMountInfo *mount_info)
{
@@ -154,6 +177,8 @@
GMountInfo *info;
GVolume *volume;
+ _the_daemon_volume_monitor = daemon_monitor;
+
daemon_monitor->mount_tracker = g_mount_tracker_new (_g_daemon_vfs_get_async_bus ());
g_signal_connect_swapped (daemon_monitor->mount_tracker, "mounted",
@@ -198,6 +223,8 @@
if (G_OBJECT_CLASS (g_daemon_volume_monitor_parent_class)->finalize)
(*G_OBJECT_CLASS (g_daemon_volume_monitor_parent_class)->finalize) (object);
+
+ _the_daemon_volume_monitor = NULL;
}
static void
@@ -211,7 +238,6 @@
GVfs *vfs;
gboolean res;
-
res = FALSE;
/* Don't do anything if the default vfs is not DAEMON_VFS */
Index: client/gdaemonvolumemonitor.h
===================================================================
--- client/gdaemonvolumemonitor.h (revision 1315)
+++ client/gdaemonvolumemonitor.h (working copy)
@@ -25,6 +25,7 @@
#include <glib-object.h>
#include <gio/gio.h>
+#include "gmounttracker.h"
G_BEGIN_DECLS
@@ -52,6 +53,8 @@
GVolumeMonitor *g_daemon_volume_monitor_new (void);
+GDaemonMount *g_daemon_volume_monitor_find_mount_by_mount_info (GMountInfo *mount_info);
+
G_END_DECLS
#endif /* __G_DAEMON_VOLUME_MONITOR_H__ */
--- /dev/null 2008-02-20 19:53:08.874023641 -0500
+++ daemon/gphoto2.mount.in 2008-01-18 12:40:00.000000000 -0500
@@ -0,0 +1,4 @@
+[Mount]
+Type=gphoto2
+Exec= libexecdir@/gvfsd-gphoto2
+AutoMount=false
--- /dev/null 2008-02-20 19:53:08.874023641 -0500
+++ daemon/gvfsbackendgphoto2.h 2008-01-18 11:59:25.000000000 -0500
@@ -0,0 +1,51 @@
+/* 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_VFS_BACKEND_GPHOTO2_H__
+#define __G_VFS_BACKEND_GPHOTO2_H__
+
+#include <gvfsbackend.h>
+
+G_BEGIN_DECLS
+
+#define G_VFS_TYPE_BACKEND_GPHOTO2 (g_vfs_backend_gphoto2_get_type ())
+#define G_VFS_BACKEND_GPHOTO2(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), G_VFS_TYPE_BACKEND_GPHOTO2, GVfsBackendGphoto2))
+#define G_VFS_BACKEND_GPHOTO2_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), G_VFS_TYPE_BACKEND_GPHOTO2, GVfsBackendGphoto2Class))
+#define G_VFS_IS_BACKEND_GPHOTO2(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), G_VFS_TYPE_BACKEND_GPHOTO2))
+#define G_VFS_IS_BACKEND_GPHOTO2_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), G_VFS_TYPE_BACKEND_GPHOTO2))
+#define G_VFS_BACKEND_GPHOTO2_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), G_VFS_TYPE_BACKEND_GPHOTO2, GVfsBackendGphoto2Class))
+
+typedef struct _GVfsBackendGphoto2 GVfsBackendGphoto2;
+typedef struct _GVfsBackendGphoto2Class GVfsBackendGphoto2Class;
+
+struct _GVfsBackendGphoto2Class
+{
+ GVfsBackendClass parent_class;
+};
+
+GType g_vfs_backend_gphoto2_get_type (void) G_GNUC_CONST;
+
+GVfsBackendGphoto2 *g_vfs_backend_gphoto2_new (void);
+
+G_END_DECLS
+
+#endif /* __G_VFS_BACKEND_GPHOTO2_H__ */
--- /dev/null 2008-02-20 19:53:08.874023641 -0500
+++ daemon/gvfsbackendgphoto2.c 2008-02-21 02:01:11.000000000 -0500
@@ -0,0 +1,1405 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+
+/* GVFS gphoto2 file system driver
+ *
+ * Copyright (C) 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>
+ */
+
+/* NOTE: since we link the libcdio libs (GPLv2) into our process space
+ * the combined work is GPLv2. This source file, however, is LGPLv2+.
+ */
+
+#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 <gphoto2.h>
+#include <libhal.h>
+#include <dbus/dbus.h>
+
+#include "gvfsbackendgphoto2.h"
+#include "gvfsjobopenforread.h"
+#include "gvfsjobread.h"
+#include "gvfsjobseekread.h"
+#include "gvfsjobqueryinfo.h"
+#include "gvfsjobenumerate.h"
+
+/*--------------------------------------------------------------------------------------------------------------*/
+
+/* TODO:
+ *
+ * - write support
+ * - be CAREFUL when adding this; need to invalidate the caches below
+ * - also need to check CameraStorageAccessType in CameraStorageInformation for
+ * whether the device supports it
+ *
+ * - support for multiple storage heads
+ * - need a device that supports this
+ * - should be different mounts so need to infect GHalVolumeMonitor with libgphoto2
+ * - probably not a huge priority to add
+ * - might help properly resolve the hack we're doing in ensure_ignore_prefix()
+ *
+ * - add payload cache
+ * - to help alleviate the fact that libgphoto2 doesn't allow partial downloads :-/
+ * - use max 25% of physical memory or at least 40MB
+ * - max file size 10% of cache or at least 20MB
+ */
+
+struct _GVfsBackendGphoto2
+{
+ GVfsBackend parent_instance;
+
+ /* a gphoto2 specific identifier for the gphoto2 camera such as usb:001,041 */
+ char *gphoto2_port;
+ GPContext *context;
+ Camera *camera;
+
+ /* see comment in ensure_ignore_prefix() */
+ char *ignore_prefix;
+
+ int num_open_files;
+
+ DBusConnection *dbus_connection;
+ LibHalContext *hal_ctx;
+ char *hal_udi;
+ char *hal_name;
+ char *hal_icon_name;
+
+ /* CACHES */
+
+ /* fully qualified path -> GFileInfo */
+ GHashTable *info_cache;
+
+ /* dir name -> CameraList of (sub-) directory names in given directory */
+ GHashTable *dir_name_cache;
+
+ /* dir name -> CameraList of file names in given directory */
+ GHashTable *file_name_cache;
+};
+
+G_DEFINE_TYPE (GVfsBackendGphoto2, g_vfs_backend_gphoto2, G_VFS_TYPE_BACKEND);
+
+static GError *
+get_error_from_gphoto2 (const char *message, int gphoto2_error_code)
+{
+ GError *error;
+ /* TODO: properly map error number */
+ error = g_error_new (G_IO_ERROR, G_IO_ERROR_FAILED, _("%s: %d: %s"),
+ message,
+ gphoto2_error_code,
+ gp_result_as_string (gphoto2_error_code));
+ return error;
+}
+
+static void
+release_device (GVfsBackendGphoto2 *gphoto2_backend)
+{
+ g_free (gphoto2_backend->gphoto2_port);
+ gphoto2_backend->gphoto2_port = NULL;
+
+ if (gphoto2_backend->context != NULL)
+ {
+ gp_context_unref (gphoto2_backend->context);
+ gphoto2_backend->context = NULL;
+ }
+
+ if (gphoto2_backend->camera != NULL)
+ {
+ gp_camera_unref (gphoto2_backend->camera);
+ gphoto2_backend->camera = NULL;
+ }
+
+ if (gphoto2_backend->dbus_connection != NULL)
+ {
+ dbus_connection_close (gphoto2_backend->dbus_connection);
+ dbus_connection_unref (gphoto2_backend->dbus_connection);
+ gphoto2_backend->dbus_connection = NULL;
+ }
+
+ if (gphoto2_backend->hal_ctx != NULL)
+ {
+ libhal_ctx_free (gphoto2_backend->hal_ctx);
+ gphoto2_backend->hal_ctx = NULL;
+
+ }
+ g_free (gphoto2_backend->hal_udi);
+ gphoto2_backend->hal_udi = NULL;
+ g_free (gphoto2_backend->hal_name);
+ gphoto2_backend->hal_name = NULL;
+ g_free (gphoto2_backend->hal_icon_name);
+ gphoto2_backend->hal_icon_name = NULL;
+
+ g_free (gphoto2_backend->ignore_prefix);
+ gphoto2_backend->ignore_prefix = NULL;
+
+ if (gphoto2_backend->info_cache != NULL)
+ {
+ g_hash_table_unref (gphoto2_backend->info_cache);
+ gphoto2_backend->info_cache = NULL;
+ }
+ if (gphoto2_backend->dir_name_cache != NULL)
+ {
+ g_hash_table_unref (gphoto2_backend->dir_name_cache);
+ gphoto2_backend->dir_name_cache = NULL;
+ }
+ if (gphoto2_backend->file_name_cache != NULL)
+ {
+ g_hash_table_unref (gphoto2_backend->file_name_cache);
+ gphoto2_backend->file_name_cache = NULL;
+ }
+}
+
+static void
+g_vfs_backend_gphoto2_finalize (GObject *object)
+{
+ GVfsBackendGphoto2 *gphoto2_backend = G_VFS_BACKEND_GPHOTO2 (object);
+
+ /*g_warning ("finalizing %p", object);*/
+
+ release_device (gphoto2_backend);
+
+ if (G_OBJECT_CLASS (g_vfs_backend_gphoto2_parent_class)->finalize)
+ (*G_OBJECT_CLASS (g_vfs_backend_gphoto2_parent_class)->finalize) (object);
+}
+
+static void
+g_vfs_backend_gphoto2_init (GVfsBackendGphoto2 *gphoto2_backend)
+{
+ GVfsBackend *backend = G_VFS_BACKEND (gphoto2_backend);
+ GMountSpec *mount_spec;
+
+ /*g_warning ("initing %p", gphoto2_backend);*/
+
+ g_vfs_backend_set_display_name (backend, "gphoto2");
+
+ mount_spec = g_mount_spec_new ("gphoto2");
+ g_vfs_backend_set_mount_spec (backend, mount_spec);
+ g_mount_spec_unref (mount_spec);
+}
+
+static char *
+compute_icon_name (GVfsBackendGphoto2 *gphoto2_backend)
+{
+ char *result;
+
+ if (gphoto2_backend->hal_icon_name == NULL)
+ {
+ result = g_strdup_printf ("camera");
+ }
+ else
+ {
+ result = g_strdup (gphoto2_backend->hal_icon_name);
+ }
+
+ return result;
+}
+
+static char *
+compute_display_name (GVfsBackendGphoto2 *gphoto2_backend)
+{
+ char *result;
+
+ if (gphoto2_backend->hal_name == NULL)
+ {
+ result = g_strdup_printf (_("Digital Camera (%s)"), gphoto2_backend->gphoto2_port);
+ }
+ else
+ {
+ result = g_strdup (gphoto2_backend->hal_name);
+ }
+
+ return result;
+}
+
+
+static void
+find_udi_for_device (GVfsBackendGphoto2 *gphoto2_backend)
+{
+ int num_camera_devices;
+ int num_mtp_devices;
+ int num_devices;
+ char **camera_devices;
+ char **mtp_devices;
+ char **devices;
+ int n, m;
+ int usb_bus_num;
+ int usb_device_num;
+ char **tokens;
+ char *endp;
+
+ gphoto2_backend->hal_udi = NULL;
+
+ /* parse the usb:001,041 string */
+
+ if (!g_str_has_prefix (gphoto2_backend->gphoto2_port, "usb:"))
+ {
+ return;
+ }
+
+ tokens = g_strsplit (gphoto2_backend->gphoto2_port + 4, ",", 0);
+ if (g_strv_length (tokens) != 2)
+ {
+ g_strfreev (tokens);
+ return;
+ }
+
+ usb_bus_num = strtol (tokens[0], &endp, 10);
+ if (*endp != '\0')
+ {
+ g_strfreev (tokens);
+ return;
+ }
+
+ usb_device_num = strtol (tokens[1], &endp, 10);
+ if (*endp != '\0')
+ {
+ g_strfreev (tokens);
+ return;
+ }
+
+ g_strfreev (tokens);
+
+ /*g_warning ("Parsed '%s' into bus=%d device=%d", gphoto2_backend->gphoto2_port, usb_bus_num, usb_device_num);*/
+
+ camera_devices = libhal_find_device_by_capability (gphoto2_backend->hal_ctx,
+ "camera",
+ &num_camera_devices,
+ NULL);
+ mtp_devices = libhal_find_device_by_capability (gphoto2_backend->hal_ctx,
+ "portable_audio_player",
+ &num_mtp_devices,
+ NULL);
+ for (m = 0; m < 2 && gphoto2_backend->hal_udi == NULL; m++)
+ {
+ devices = m == 0 ? camera_devices : mtp_devices;
+ num_devices = m == 0 ? num_camera_devices : num_mtp_devices;
+
+ if (devices != NULL)
+ {
+ for (n = 0; n < num_devices && gphoto2_backend->hal_udi == NULL; n++)
+ {
+ char *udi = devices[n];
+ LibHalPropertySet *ps;
+
+ ps = libhal_device_get_all_properties (gphoto2_backend->hal_ctx, udi, NULL);
+ if (ps != NULL)
+ {
+ const char *subsystem;
+
+ subsystem = libhal_ps_get_string (ps, "info.subsystem");
+ if (subsystem != NULL && strcmp (subsystem, "usb") == 0)
+ {
+ int device_usb_bus_num;
+ int device_usb_device_num;
+ const char *icon_from_hal;
+ const char *name_from_hal;
+
+ device_usb_bus_num = libhal_ps_get_int32 (ps, "usb.bus_number");
+ device_usb_device_num = libhal_ps_get_int32 (ps, "usb.linux.device_number");
+ icon_from_hal = libhal_ps_get_string (ps, "info.desktop.icon");
+ name_from_hal = libhal_ps_get_string (ps, "info.desktop.name");
+
+ /*g_warning ("looking at usb device '%s' with bus=%d, device=%d",
+ udi, device_usb_bus_num, device_usb_device_num);*/
+
+ if (device_usb_bus_num == usb_bus_num &&
+ device_usb_device_num == usb_device_num)
+ {
+ char *name;
+ const char *parent_udi;
+ LibHalPropertySet *ps2;
+
+ /*g_warning ("udi '%s' is the one!", gphoto2_backend->hal_udi);*/
+
+ /* IMPORTANT:
+ *
+ * Keep this naming code in sync with
+ *
+ * hal/ghalvolume;do_update_from_hal_for_camera()
+ */
+ name = NULL;
+ parent_udi = libhal_ps_get_string (ps, "info.parent");
+ if (name_from_hal != NULL)
+ {
+ name = g_strdup (name_from_hal);
+ }
+ else if (parent_udi != NULL)
+ {
+ ps2 = libhal_device_get_all_properties (gphoto2_backend->hal_ctx, parent_udi, NULL);
+ if (ps2 != NULL)
+ {
+ const char *vendor;
+ const char *product;
+
+ vendor = libhal_ps_get_string (ps2, "usb_device.vendor");
+ product = libhal_ps_get_string (ps2, "usb_device.product");
+ if (vendor == NULL)
+ {
+ if (product != NULL)
+ name = g_strdup (product);
+ }
+ else
+ {
+ if (product != NULL)
+ name = g_strdup_printf ("%s %s", vendor, product);
+ else
+ name = g_strdup_printf (_("%s Camera"), vendor);
+ }
+ libhal_free_property_set (ps2);
+ }
+ }
+ if (name == NULL)
+ name = g_strdup (_("Camera"));
+
+ gphoto2_backend->hal_udi = g_strdup (udi);
+ gphoto2_backend->hal_name = name;
+ if (icon_from_hal != NULL)
+ {
+ gphoto2_backend->hal_icon_name = g_strdup (icon_from_hal);
+ }
+ else
+ {
+ if (m == 1)
+ {
+ gphoto2_backend->hal_icon_name = g_strdup ("multimedia-player");
+ }
+ else
+ {
+ gphoto2_backend->hal_icon_name = g_strdup ("camera");
+ }
+ }
+ }
+
+ }
+
+ libhal_free_property_set (ps);
+ }
+ }
+ libhal_free_string_array (devices);
+ }
+ }
+}
+
+static void
+_hal_device_removed (LibHalContext *hal_ctx, const char *udi)
+{
+ GVfsBackendGphoto2 *gphoto2_backend;
+
+ gphoto2_backend = G_VFS_BACKEND_GPHOTO2 (libhal_ctx_get_user_data (hal_ctx));
+
+ if (gphoto2_backend->hal_udi != NULL && strcmp (udi, gphoto2_backend->hal_udi) == 0)
+ {
+ /*g_warning ("we have been removed!");*/
+
+ /* TODO: need a cleaner way to force unmount ourselves */
+ exit (1);
+ }
+}
+
+static void
+do_mount (GVfsBackend *backend,
+ GVfsJobMount *job,
+ GMountSpec *mount_spec,
+ GMountSource *mount_source,
+ gboolean is_automount)
+{
+ char *fuse_name;
+ char *display_name;
+ char *icon_name;
+ const char *host;
+ GVfsBackendGphoto2 *gphoto2_backend = G_VFS_BACKEND_GPHOTO2 (backend);
+ GError *error = NULL;
+ GMountSpec *gphoto2_mount_spec;
+ int rc;
+ GPPortInfo info;
+ GPPortInfoList *il = NULL;
+ int n;
+ DBusError dbus_error;
+
+ /*g_warning ("do_mount %p", gphoto2_backend);*/
+
+ /* setup libhal */
+
+ dbus_error_init (&dbus_error);
+ gphoto2_backend->dbus_connection = dbus_bus_get_private (DBUS_BUS_SYSTEM, &dbus_error);
+ if (dbus_error_is_set (&dbus_error))
+ {
+ release_device (gphoto2_backend);
+ dbus_error_free (&dbus_error);
+ g_set_error (&error, G_IO_ERROR, G_IO_ERROR_FAILED, _("Cannot connect to the system bus"));
+ g_vfs_job_failed_from_error (G_VFS_JOB (job), error);
+ g_error_free (error);
+ return;
+ }
+
+ gphoto2_backend->hal_ctx = libhal_ctx_new ();
+ if (gphoto2_backend->hal_ctx == NULL)
+ {
+ release_device (gphoto2_backend);
+ g_set_error (&error, G_IO_ERROR, G_IO_ERROR_FAILED, _("Cannot create libhal context"));
+ g_vfs_job_failed_from_error (G_VFS_JOB (job), error);
+ g_error_free (error);
+ return;
+ }
+
+ _g_dbus_connection_integrate_with_main (gphoto2_backend->dbus_connection);
+ libhal_ctx_set_dbus_connection (gphoto2_backend->hal_ctx, gphoto2_backend->dbus_connection);
+
+ if (!libhal_ctx_init (gphoto2_backend->hal_ctx, &dbus_error))
+ {
+ release_device (gphoto2_backend);
+ dbus_error_free (&dbus_error);
+ g_set_error (&error, G_IO_ERROR, G_IO_ERROR_FAILED, _("Cannot initialize libhal"));
+ g_vfs_job_failed_from_error (G_VFS_JOB (job), error);
+ g_error_free (error);
+ return;
+ }
+
+ libhal_ctx_set_device_removed (gphoto2_backend->hal_ctx, _hal_device_removed);
+ libhal_ctx_set_user_data (gphoto2_backend->hal_ctx, gphoto2_backend);
+
+ /* setup gphoto2 */
+
+ host = g_mount_spec_get (mount_spec, "host");
+ /*g_warning ("host='%s'", host);*/
+ if (host == NULL || strlen (host) < 3 || host[0] != '[' || host[strlen (host) - 1] != ']')
+ {
+ g_set_error (&error, G_IO_ERROR, G_IO_ERROR_FAILED, _("No drive specified"));
+ g_vfs_job_failed_from_error (G_VFS_JOB (job), error);
+ g_error_free (error);
+ release_device (gphoto2_backend);
+ return;
+ }
+
+ gphoto2_backend->gphoto2_port = g_strdup (host + 1);
+ gphoto2_backend->gphoto2_port[strlen (gphoto2_backend->gphoto2_port) - 1] = '\0';
+
+ /*g_warning ("decoded host='%s'", gphoto2_backend->gphoto2_port);*/
+
+ find_udi_for_device (gphoto2_backend);
+
+ gphoto2_backend->context = gp_context_new ();
+ if (gphoto2_backend->context == NULL)
+ {
+ g_set_error (&error, G_IO_ERROR, G_IO_ERROR_FAILED, _("Cannot create gphoto2 context"));
+ g_vfs_job_failed_from_error (G_VFS_JOB (job), error);
+ g_error_free (error);
+ release_device (gphoto2_backend);
+ return;
+ }
+
+ rc = gp_camera_new (&(gphoto2_backend->camera));
+ if (rc != 0)
+ {
+ error = get_error_from_gphoto2 ("Error creating camera", rc);
+ g_vfs_job_failed_from_error (G_VFS_JOB (job), error);
+ g_error_free (error);
+ release_device (gphoto2_backend);
+ return;
+ }
+
+
+ il = NULL;
+
+ rc = gp_port_info_list_new (&il);
+ if (rc != 0)
+ {
+ error = get_error_from_gphoto2 ("Error creating port info list", rc);
+ g_vfs_job_failed_from_error (G_VFS_JOB (job), error);
+ g_error_free (error);
+ release_device (gphoto2_backend);
+ return;
+ }
+
+ rc = gp_port_info_list_load (il);
+ if (rc != 0)
+ {
+ error = get_error_from_gphoto2 ("Error loading info list", rc);
+ g_vfs_job_failed_from_error (G_VFS_JOB (job), error);
+ g_error_free (error);
+ release_device (gphoto2_backend);
+ return;
+ }
+
+ /*g_warning ("gphoto2_port='%s'", gphoto2_backend->gphoto2_port);*/
+
+ n = gp_port_info_list_lookup_path (il, gphoto2_backend->gphoto2_port);
+ if (n == GP_ERROR_UNKNOWN_PORT)
+ {
+ error = get_error_from_gphoto2 ("Error looking up port from port info list", rc);
+ g_vfs_job_failed_from_error (G_VFS_JOB (job), error);
+ g_error_free (error);
+ release_device (gphoto2_backend);
+ return;
+ }
+
+ rc = gp_port_info_list_get_info (il, n, &info);
+ if (rc != 0)
+ {
+ error = get_error_from_gphoto2 ("Error getting port info from port info list", rc);
+ g_vfs_job_failed_from_error (G_VFS_JOB (job), error);
+ g_error_free (error);
+ release_device (gphoto2_backend);
+ return;
+ }
+
+ /*g_warning ("'%s' '%s' '%s'", info.name, info.path, info.library_filename);*/
+
+ /* set port */
+ rc = gp_camera_set_port_info (gphoto2_backend->camera, info);
+ if (rc != 0)
+ {
+ error = get_error_from_gphoto2 ("Error setting port info", rc);
+ g_vfs_job_failed_from_error (G_VFS_JOB (job), error);
+ g_error_free (error);
+ release_device (gphoto2_backend);
+ return;
+ }
+ gp_port_info_list_free(il);
+
+ rc = gp_camera_init (gphoto2_backend->camera, gphoto2_backend->context);
+ if (rc != 0)
+ {
+ error = get_error_from_gphoto2 ("Error initializing camera", rc);
+ g_vfs_job_failed_from_error (G_VFS_JOB (job), error);
+ g_error_free (error);
+ release_device (gphoto2_backend);
+ return;
+ }
+
+ fuse_name = g_strdup_printf (_("gphoto2 mount on %s"), gphoto2_backend->gphoto2_port);
+ icon_name = compute_icon_name (gphoto2_backend);
+ display_name = compute_display_name (gphoto2_backend);
+ g_vfs_backend_set_stable_name (backend, fuse_name);
+ g_vfs_backend_set_display_name (backend, display_name);
+ g_vfs_backend_set_icon_name (backend, icon_name);
+ g_free (display_name);
+ g_free (icon_name);
+ g_free (fuse_name);
+
+ g_vfs_job_succeeded (G_VFS_JOB (job));
+
+ gphoto2_mount_spec = g_mount_spec_new ("gphoto2");
+ g_mount_spec_set (gphoto2_mount_spec, "host", host);
+ g_vfs_backend_set_mount_spec (backend, gphoto2_mount_spec);
+ g_mount_spec_unref (gphoto2_mount_spec);
+
+ gphoto2_backend->info_cache = g_hash_table_new_full (g_str_hash,
+ g_str_equal,
+ g_free,
+ g_object_unref);
+
+ gphoto2_backend->dir_name_cache = g_hash_table_new_full (g_str_hash,
+ g_str_equal,
+ g_free,
+ (GDestroyNotify) gp_list_unref);
+
+ gphoto2_backend->file_name_cache = g_hash_table_new_full (g_str_hash,
+ g_str_equal,
+ g_free,
+ (GDestroyNotify) gp_list_unref);
+
+ /*g_warning ("mounted %p", gphoto2_backend);*/
+}
+
+static gboolean
+try_mount (GVfsBackend *backend,
+ GVfsJobMount *job,
+ GMountSpec *mount_spec,
+ GMountSource *mount_source,
+ gboolean is_automount)
+{
+ const char *host;
+ GError *error = NULL;
+ GMountSpec *gphoto2_mount_spec;
+
+ /*g_warning ("try_mount %p", backend);*/
+
+ /* TODO: Hmm.. apparently we have to set the mount spec in
+ * try_mount(); doing it in mount() do_won't work..
+ */
+ host = g_mount_spec_get (mount_spec, "host");
+ /*g_warning ("tm host=%s", host);*/
+ if (host == NULL)
+ {
+ g_set_error (&error, G_IO_ERROR, G_IO_ERROR_FAILED, _("No camera specified"));
+ g_vfs_job_failed_from_error (G_VFS_JOB (job), error);
+ g_error_free (error);
+ return TRUE;
+ }
+
+ gphoto2_mount_spec = g_mount_spec_new ("gphoto2");
+ g_mount_spec_set (gphoto2_mount_spec, "host", host);
+ g_vfs_backend_set_mount_spec (backend, gphoto2_mount_spec);
+ g_mount_spec_unref (gphoto2_mount_spec);
+ return FALSE;
+}
+
+
+static void
+do_unmount (GVfsBackend *backend,
+ GVfsJobUnmount *job)
+{
+ GError *error;
+ GVfsBackendGphoto2 *gphoto2_backend = G_VFS_BACKEND_GPHOTO2 (backend);
+
+ if (gphoto2_backend->num_open_files > 0)
+ {
+ error = g_error_new (G_IO_ERROR, G_IO_ERROR_BUSY,
+ _("File system is busy: %d open files"), gphoto2_backend->num_open_files);
+ g_vfs_job_failed_from_error (G_VFS_JOB (job), error);
+ return;
+ }
+
+ release_device (gphoto2_backend);
+ g_vfs_job_succeeded (G_VFS_JOB (job));
+
+ //g_warning ("unmounted %p", backend);
+}
+
+/* The PTP gphoto2 backend puts an annoying virtual store_00010001
+ * directory in the root (in fact 00010001 can be any hexedecimal
+ * digit).
+ *
+ * We want to skip that as the x-content detection expects to find the
+ * DCIM/ folder. As such, this function tries to detect the presence
+ * of such a folder in the root and, if found, sets a variable that is
+ * prepended to any path passed to libgphoto2. This is cached for as
+ * long as we got a connection to libgphoto2. If this operation fails
+ * then the passed job will be cancelled and this function will return
+ * FALSE.
+ *
+ * IMPORTANT: *ANY* method called by the gvfs core needs to call this
+ * function before doing anything. If FALSE is returned the function
+ * just needs to return to the gvfs core.
+ */
+static gboolean
+ensure_ignore_prefix (GVfsBackendGphoto2 *gphoto2_backend, GVfsJob *job)
+{
+ int rc;
+ char *prefix;
+ GError *error;
+ CameraList *list;
+
+ /* already set */
+ if (gphoto2_backend->ignore_prefix != NULL)
+ return TRUE;
+
+ /* check folders in / - if there is exactly one folder of the form "store_" followed by eight
+ * hexadecimal digits.. then use that as ignore_prefix.. otherwise don't use anything
+ */
+
+ gp_list_new (&list);
+ rc = gp_camera_folder_list_folders (gphoto2_backend->camera,
+ "/",
+ list,
+ gphoto2_backend->context);
+ if (rc != 0)
+ {
+ error = get_error_from_gphoto2 ("Error listing folders to figure out ignore prefix", rc);
+ g_vfs_job_failed_from_error (job, error);
+ return FALSE;
+ }
+
+ prefix = NULL;
+
+ if (gp_list_count (list) == 1)
+ {
+ char *name;
+ const char *s;
+ unsigned int n;
+
+ gp_list_get_name (list, 0, &s);
+
+ name = g_ascii_strdown (s, -1);
+ if (g_str_has_prefix (name, "store_") && strlen (name) == 14)
+ {
+ for (n = 6; n < 14; n++)
+ {
+ if (!g_ascii_isxdigit (name[n]))
+ {
+ break;
+ }
+ }
+ if (n == 14)
+ {
+ prefix = g_strconcat ("/", name, NULL);
+ }
+ }
+
+ g_free (name);
+ }
+ gp_list_free (list);
+
+ if (prefix == NULL)
+ gphoto2_backend->ignore_prefix = g_strdup ("");
+ else
+ gphoto2_backend->ignore_prefix = prefix;
+
+ return TRUE;
+}
+
+typedef struct {
+ CameraFile *file;
+ const char *data;
+ unsigned long int size;
+ unsigned long int cursor;
+} ReadHandle;
+
+static void
+free_read_handle (ReadHandle *read_handle)
+{
+ if (read_handle->file != NULL)
+ gp_file_unref (read_handle->file);
+ g_free (read_handle);
+}
+
+static void
+do_open_for_read (GVfsBackend *backend,
+ GVfsJobOpenForRead *job,
+ const char *filename)
+{
+ int rc;
+ GError *error;
+ ReadHandle *read_handle;
+ GVfsBackendGphoto2 *gphoto2_backend = G_VFS_BACKEND_GPHOTO2 (backend);
+ char *s;
+ char *dir;
+ char *name;
+
+ if (!ensure_ignore_prefix (gphoto2_backend, G_VFS_JOB (job)))
+ return;
+
+ s = g_path_get_dirname (filename);
+ dir = g_strconcat (gphoto2_backend->ignore_prefix, s, NULL);
+ g_free (s);
+ name = g_path_get_basename (filename);
+
+ /*g_warning ("open_for_read (%s)", filename);*/
+
+ read_handle = g_new0 (ReadHandle, 1);
+ rc = gp_file_new (&read_handle->file);
+ if (rc != 0)
+ {
+ error = get_error_from_gphoto2 ("Error creating file", rc);
+ g_vfs_job_failed_from_error (G_VFS_JOB (job), error);
+ free_read_handle (read_handle);
+ goto out;
+ }
+
+ rc = gp_camera_file_get (gphoto2_backend->camera,
+ dir,
+ name,
+ GP_FILE_TYPE_NORMAL,
+ read_handle->file,
+ gphoto2_backend->context);
+ if (rc != 0)
+ {
+ error = get_error_from_gphoto2 ("Error getting file", rc);
+ g_vfs_job_failed_from_error (G_VFS_JOB (job), error);
+ free_read_handle (read_handle);
+ goto out;
+ }
+
+ rc = gp_file_get_data_and_size (read_handle->file, &read_handle->data, &read_handle->size);
+ if (rc != 0)
+ {
+ error = get_error_from_gphoto2 ("Error getting data from file", rc);
+ g_vfs_job_failed_from_error (G_VFS_JOB (job), error);
+ free_read_handle (read_handle);
+ goto out;
+ }
+
+ /*g_warning ("data=%p size=%ld", read_handle->data, read_handle->size);*/
+
+ gphoto2_backend->num_open_files++;
+
+ read_handle->cursor = 0;
+
+ g_vfs_job_open_for_read_set_can_seek (job, TRUE);
+ g_vfs_job_open_for_read_set_handle (job, GINT_TO_POINTER (read_handle));
+ g_vfs_job_succeeded (G_VFS_JOB (job));
+
+ out:
+ g_free (name);
+ g_free (dir);
+}
+
+static void
+do_read (GVfsBackend *backend,
+ GVfsJobRead *job,
+ GVfsBackendHandle handle,
+ char *buffer,
+ gsize bytes_requested)
+{
+ //GVfsBackendGphoto2 *gphoto2_backend = G_VFS_BACKEND_GPHOTO2 (backend);
+ ReadHandle *read_handle = (ReadHandle *) handle;
+ gsize bytes_left;
+ gsize bytes_to_copy;
+
+ /*g_warning ("do_read (%d @ %ld of %ld)", bytes_requested, read_handle->cursor, read_handle->size);*/
+
+ if (read_handle->cursor >= read_handle->size)
+ {
+ bytes_to_copy = 0;
+ goto out;
+ }
+
+ bytes_left = read_handle->size - read_handle->cursor;
+ if (bytes_requested > bytes_left)
+ bytes_to_copy = bytes_left;
+ else
+ bytes_to_copy = bytes_requested;
+
+ memcpy (buffer, read_handle->data + read_handle->cursor, bytes_to_copy);
+ read_handle->cursor += bytes_to_copy;
+
+ out:
+
+ g_vfs_job_read_set_size (job, bytes_to_copy);
+ g_vfs_job_succeeded (G_VFS_JOB (job));
+}
+
+static void
+do_seek_on_read (GVfsBackend *backend,
+ GVfsJobSeekRead *job,
+ GVfsBackendHandle handle,
+ goffset offset,
+ GSeekType type)
+{
+ GVfsBackendGphoto2 *gphoto2_backend = G_VFS_BACKEND_GPHOTO2 (backend);
+ ReadHandle *read_handle = (ReadHandle *) handle;
+ long new_offset;
+
+ /*g_warning ("seek_on_read (%d, %d)", (int)offset, type);*/
+
+ switch (type)
+ {
+ default:
+ case G_SEEK_SET:
+ new_offset = offset;
+ break;
+ case G_SEEK_CUR:
+ new_offset = read_handle->cursor + offset;
+ break;
+ case G_SEEK_END:
+ new_offset = read_handle->size + offset;
+ break;
+ }
+
+ if (new_offset < 0 || new_offset >= read_handle->size)
+ {
+ g_vfs_job_failed (G_VFS_JOB (job), G_IO_ERROR,
+ G_IO_ERROR_FAILED,
+ _("Error seeking in stream on camera %s"), gphoto2_backend->gphoto2_port);
+ }
+ else
+ {
+ read_handle->cursor = new_offset;
+
+ g_vfs_job_seek_read_set_offset (job, offset);
+ g_vfs_job_succeeded (G_VFS_JOB (job));
+ }
+}
+
+static void
+do_close_read (GVfsBackend *backend,
+ GVfsJobCloseRead *job,
+ GVfsBackendHandle handle)
+{
+ ReadHandle *read_handle = (ReadHandle *) handle;
+ GVfsBackendGphoto2 *gphoto2_backend = G_VFS_BACKEND_GPHOTO2 (backend);
+
+ /*g_warning ("close ()");*/
+
+ free_read_handle (read_handle);
+
+ gphoto2_backend->num_open_files--;
+
+ g_vfs_job_succeeded (G_VFS_JOB (job));
+}
+
+/* the passed 'dir' variable contains ignore_prefix */
+static gboolean
+_set_info (GVfsBackendGphoto2 *gphoto2_backend, const char *dir, const char *name, GFileInfo *info, GError **error)
+{
+ int rc;
+ gboolean ret;
+ CameraFileInfo gp_info;
+ char *full_path;
+ GFileInfo *cached_info;
+ GTimeVal mtime;
+ char *mime_type;
+ GIcon *icon;
+
+ /*g_warning ("_set_info(); dir='%s', name='%s'", dir, name);*/
+
+ ret = FALSE;
+
+ /* look up cache */
+ full_path = g_strconcat (dir, "/", name, NULL);
+ cached_info = g_hash_table_lookup (gphoto2_backend->info_cache, full_path);
+ if (cached_info != NULL)
+ {
+ /*g_warning ("Using cached info for '%s'", full_path);*/
+ g_file_info_copy_into (cached_info, info);
+ ret = TRUE;
+ goto out;
+ }
+
+ rc = gp_camera_file_get_info (gphoto2_backend->camera,
+ dir,
+ name,
+ &gp_info,
+ gphoto2_backend->context);
+ if (rc != 0)
+ {
+ CameraList *list;
+ unsigned int n;
+
+ /* gphoto2 doesn't know about this file.. it may be a folder; try that */
+
+ gp_list_new (&list);
+ rc = gp_camera_folder_list_folders (gphoto2_backend->camera,
+ dir,
+ list,
+ gphoto2_backend->context);
+ if (rc != 0)
+ {
+ *error = get_error_from_gphoto2 ("Error listing folders", rc);
+ goto out;
+ }
+
+ for (n = 0; n < gp_list_count (list); n++)
+ {
+ const char *folder_name;
+
+ gp_list_get_name (list, n, &folder_name);
+
+ /*g_warning ("Looking at folder_name='%s' for dir='%s'", folder_name, dir);*/
+
+ if (strcmp (name, folder_name) != 0)
+ continue;
+
+ /*g_warning ("Got it");*/
+
+ g_file_info_set_name (info, name);
+ g_file_info_set_display_name (info, name);
+ icon = g_themed_icon_new ("folder");
+ g_file_info_set_icon (info, icon);
+ g_object_unref (icon);
+ 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);
+ 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, FALSE);
+ 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);
+
+ gp_list_free (list);
+ ret = TRUE;
+ goto add_to_cache;
+ }
+ gp_list_free (list);
+
+ /* nope, not a folder either.. error out.. */
+
+ *error = get_error_from_gphoto2 ("Error getting file info", rc);
+ goto out;
+ }
+
+ g_file_info_set_name (info, name);
+ g_file_info_set_display_name (info, name);
+ g_file_info_set_file_type (info, G_FILE_TYPE_REGULAR);
+
+ if (gp_info.file.fields & GP_FILE_INFO_SIZE)
+ {
+ g_file_info_set_size (info, gp_info.file.size);
+ }
+ else
+ {
+ /* not really sure this is the right thing to do... */
+ g_file_info_set_size (info, 0);
+ }
+
+ /* TODO: We really should sniff the file / look at file extensions
+ * instead of relying on gp_info.file.type... but sniffing the file
+ * is no fun since we (currently) can't do partial reads with the
+ * libgphoto2 API :-/
+ */
+ mime_type = NULL;
+ if (gp_info.file.fields & GP_FILE_INFO_TYPE)
+ {
+ /* Nasty (or cute?) hack to get correct mime types on my Sansa
+ * Sandisk player; it returns bogus "application/x-unknown" mime
+ * type. Go Sandisk!
+ */
+ if (strcmp (gp_info.file.type, "application/x-unknown") == 0)
+ {
+ mime_type = g_content_type_guess (name, NULL, 0, NULL);
+#if 0
+ if (g_str_has_suffix (name, ".mp3") || g_str_has_suffix (name, ".MP3"))
+ {
+ mime_type = g_strdup ("audio/mpeg");
+ }
+ else if (g_str_has_suffix (name, ".mov") || g_str_has_suffix (name, ".MOV"))
+ {
+ mime_type = g_strdup ("video/quicktime");
+ }
+ else
+ {
+ /* application/x-unknown is bogus; fall back to default specified below */
+ }
+#endif
+ }
+ else
+ {
+ mime_type = g_strdup (gp_info.file.type);
+ }
+ }
+ if (mime_type == NULL)
+ mime_type = g_strdup ("application/octet-stream");
+ g_file_info_set_content_type (info, mime_type);
+
+ icon = g_content_type_get_icon (mime_type);
+ /*g_warning ("got icon %p for mime_type '%s'", icon, mime_type);*/
+ if (icon != NULL)
+ {
+ g_file_info_set_icon (info, icon);
+ g_object_unref (icon);
+ }
+ g_free (mime_type);
+
+
+ if (gp_info.file.fields & GP_FILE_INFO_MTIME)
+ mtime.tv_sec = gp_info.file.mtime;
+ else
+ mtime.tv_sec = 0;
+ mtime.tv_usec = 0;
+ g_file_info_set_modification_time (info, &mtime);
+
+ 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, FALSE);
+ 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, FALSE);
+ 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);
+
+ ret = TRUE;
+
+ add_to_cache:
+ /* add this sucker to the cache */
+ if (ret == TRUE)
+ {
+ g_hash_table_insert (gphoto2_backend->info_cache, g_strdup (full_path), g_file_info_dup (info));
+ }
+
+ out:
+ g_free (full_path);
+ return ret;
+}
+
+static void
+do_query_info (GVfsBackend *backend,
+ GVfsJobQueryInfo *job,
+ const char *filename,
+ GFileQueryInfoFlags flags,
+ GFileInfo *info,
+ GFileAttributeMatcher *matcher)
+{
+ GVfsBackendGphoto2 *gphoto2_backend = G_VFS_BACKEND_GPHOTO2 (backend);
+ GError *error;
+
+ if (!ensure_ignore_prefix (gphoto2_backend, G_VFS_JOB (job)))
+ return;
+
+ /*g_warning ("get_file_info (%s)", filename);*/
+
+ if (strcmp (filename, "/") == 0)
+ {
+ GIcon *icon;
+ char *display_name;
+ display_name = compute_display_name (gphoto2_backend);
+ g_file_info_set_display_name (info, display_name);
+ g_free (display_name);
+ 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);
+ icon = g_themed_icon_new ("folder");
+ g_file_info_set_icon (info, 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, FALSE);
+ 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_object_unref (icon);
+ g_vfs_job_succeeded (G_VFS_JOB (job));
+ }
+ else
+ {
+ char *s;
+ char *dir;
+ char *name;
+
+ s = g_path_get_dirname (filename);
+ dir = g_strconcat (gphoto2_backend->ignore_prefix, s, NULL);
+ g_free (s);
+ name = g_path_get_basename (filename);
+
+ if (!_set_info (gphoto2_backend, dir, name, info, &error))
+ {
+ g_vfs_job_failed_from_error (G_VFS_JOB (job), error);
+ }
+ else
+ {
+ g_vfs_job_succeeded (G_VFS_JOB (job));
+ }
+
+ g_free (name);
+ g_free (dir);
+ }
+
+}
+
+static void
+do_enumerate (GVfsBackend *backend,
+ GVfsJobEnumerate *job,
+ const char *given_filename,
+ GFileAttributeMatcher *matcher,
+ GFileQueryInfoFlags flags)
+{
+ GVfsBackendGphoto2 *gphoto2_backend = G_VFS_BACKEND_GPHOTO2 (backend);
+ GFileInfo *info;
+ GList *l;
+ GError *error;
+ CameraList *list;
+ int n;
+ int rc;
+ char *filename;
+ gboolean using_cached_dir_list;
+ gboolean using_cached_file_list;
+
+ if (!ensure_ignore_prefix (gphoto2_backend, G_VFS_JOB (job)))
+ return;
+
+ l = NULL;
+ using_cached_dir_list = FALSE;
+ using_cached_file_list = FALSE;
+
+ filename = g_strconcat (gphoto2_backend->ignore_prefix, given_filename, NULL);
+ /*g_warning ("enumerate (%s) (%s)", given_filename, filename);*/
+
+ /* first, list the folders */
+ list = g_hash_table_lookup (gphoto2_backend->dir_name_cache, filename);
+ if (list == NULL)
+ {
+ gp_list_new (&list);
+ rc = gp_camera_folder_list_folders (gphoto2_backend->camera,
+ filename,
+ list,
+ gphoto2_backend->context);
+ if (rc != 0)
+ {
+ error = get_error_from_gphoto2 ("Error listing folders", rc);
+ g_vfs_job_failed_from_error (G_VFS_JOB (job), error);
+ g_free (filename);
+ return;
+ }
+ }
+ else
+ {
+ /*g_warning ("Using cached dirlist for dir '%s'", filename);*/
+ gp_list_ref (list);
+ using_cached_dir_list = TRUE;
+ }
+ for (n = 0; n < gp_list_count (list); n++)
+ {
+ const char *name;
+ GIcon *icon;
+
+ gp_list_get_name (list, n, &name);
+
+ /*g_warning ("enum '%s'", name);*/
+
+ info = g_file_info_new ();
+ g_file_info_set_name (info, name);
+ g_file_info_set_display_name (info, name);
+
+ icon = g_themed_icon_new ("folder");
+ g_file_info_set_icon (info, icon);
+ g_object_unref (icon);
+
+ 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);
+ 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, FALSE);
+ 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);
+
+ l = g_list_append (l, info);
+ }
+ if (!using_cached_dir_list)
+ {
+ gp_list_ref (list);
+ g_hash_table_insert (gphoto2_backend->dir_name_cache, g_strdup (filename), list);
+ }
+ gp_list_unref (list);
+
+
+ /* then list the files in each folder */
+ list = g_hash_table_lookup (gphoto2_backend->file_name_cache, filename);
+ if (list == NULL)
+ {
+ gp_list_new (&list);
+ rc = gp_camera_folder_list_files (gphoto2_backend->camera,
+ filename,
+ list,
+ gphoto2_backend->context);
+ if (rc != 0)
+ {
+ error = get_error_from_gphoto2 ("Error listing files in folder", rc);
+ g_vfs_job_failed_from_error (G_VFS_JOB (job), error);
+ g_free (filename);
+ return;
+ }
+ }
+ else
+ {
+ /*g_warning ("Using cached file list for dir '%s'", filename);*/
+ gp_list_ref (list);
+ using_cached_file_list = TRUE;
+ }
+ for (n = 0; n < gp_list_count (list); n++)
+ {
+ const char *name;
+
+ gp_list_get_name (list, n, &name);
+
+ info = g_file_info_new ();
+ if (!_set_info (gphoto2_backend, filename, name, info, &error))
+ {
+ g_vfs_job_failed_from_error (G_VFS_JOB (job), error);
+ g_list_foreach (l, (GFunc) g_object_unref, NULL);
+ g_list_free (l);
+ gp_list_free (list);
+ return;
+ }
+ l = g_list_append (l, info);
+ }
+ if (!using_cached_file_list)
+ {
+ gp_list_ref (list);
+ g_hash_table_insert (gphoto2_backend->file_name_cache, g_strdup (filename), list);
+ }
+ gp_list_unref (list);
+
+ /* and we're done */
+
+ g_vfs_job_succeeded (G_VFS_JOB (job));
+ g_vfs_job_enumerate_add_infos (job, l);
+ g_list_foreach (l, (GFunc) g_object_unref, NULL);
+ g_list_free (l);
+ g_vfs_job_enumerate_done (job);
+
+ g_free (filename);
+}
+
+static void
+do_query_fs_info (GVfsBackend *backend,
+ GVfsJobQueryFsInfo *job,
+ const char *filename,
+ GFileInfo *info,
+ GFileAttributeMatcher *attribute_matcher)
+{
+ int rc;
+ GVfsBackendGphoto2 *gphoto2_backend = G_VFS_BACKEND_GPHOTO2 (backend);
+
+ g_file_info_set_attribute_string (info, G_FILE_ATTRIBUTE_FILESYSTEM_TYPE, "gphoto2");
+ g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_FILESYSTEM_READONLY, TRUE);
+ g_file_info_set_attribute_uint32 (info, G_FILE_ATTRIBUTE_FILESYSTEM_USE_PREVIEW, G_FILESYSTEM_PREVIEW_TYPE_IF_LOCAL);
+
+ int num_storage_info;
+ CameraStorageInformation *storage_info;
+
+ rc = gp_camera_get_storageinfo (gphoto2_backend->camera, &storage_info, &num_storage_info, gphoto2_backend->context);
+ if (rc == 0)
+ {
+ if (num_storage_info >= 1)
+ {
+
+ /*g_warning ("capacity = %ld", storage_info[0].capacitykbytes);*/
+ /*g_warning ("free = %ld", storage_info[0].freekbytes);*/
+
+ /* for now we only support a single storage head */
+ if (storage_info[0].fields & GP_STORAGEINFO_MAXCAPACITY)
+ {
+ g_file_info_set_attribute_uint64 (info,
+ G_FILE_ATTRIBUTE_FILESYSTEM_SIZE,
+ storage_info[0].capacitykbytes * 1024);
+ }
+ if (storage_info[0].fields & GP_STORAGEINFO_FREESPACEKBYTES)
+ {
+ g_file_info_set_attribute_uint64 (info,
+ G_FILE_ATTRIBUTE_FILESYSTEM_FREE,
+ storage_info[0].freekbytes * 1024);
+ }
+
+ }
+ /*g_warning ("got %d storage_info objects", num_storage_info);*/
+ }
+
+
+ g_vfs_job_succeeded (G_VFS_JOB (job));
+}
+
+static void
+g_vfs_backend_gphoto2_class_init (GVfsBackendGphoto2Class *klass)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+ GVfsBackendClass *backend_class = G_VFS_BACKEND_CLASS (klass);
+
+ gobject_class->finalize = g_vfs_backend_gphoto2_finalize;
+
+ backend_class->try_mount = try_mount;
+ backend_class->mount = do_mount;
+ backend_class->unmount = do_unmount;
+ backend_class->open_for_read = do_open_for_read;
+ backend_class->read = do_read;
+ backend_class->seek_on_read = do_seek_on_read;
+ backend_class->close_read = do_close_read;
+ backend_class->query_info = do_query_info;
+ backend_class->enumerate = do_enumerate;
+ backend_class->query_fs_info = do_query_fs_info;
+}
Index: gio/glocalfileinfo.c
===================================================================
--- gio/glocalfileinfo.c (revision 6545)
+++ gio/glocalfileinfo.c (working copy)
@@ -1,3 +1,5 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+
/* GIO - GLib Input, Output and Streaming Library
*
* Copyright (C) 2006-2007 Red Hat, Inc.
@@ -1488,48 +1490,31 @@
if (g_file_attribute_matcher_matches (attribute_matcher,
G_FILE_ATTRIBUTE_STANDARD_ICON))
{
- char *mimetype_icon, *generic_mimetype_icon, *type_icon, *p;
- char *icon_names[3];
GIcon *icon;
- int i;
- mimetype_icon = g_strdup (content_type);
-
- while ((p = strchr (mimetype_icon, '/')) != NULL)
- *p = '-';
+ icon = g_content_type_get_icon (content_type);
+ if (icon != NULL)
+ {
+ if (g_type_is_a (G_OBJECT_TYPE (icon), G_TYPE_THEMED_ICON))
+ {
+ const char *type_icon;
- p = strchr (content_type, '/');
- if (p == NULL)
- p = content_type + strlen (content_type);
+ /* TODO: Special case desktop dir? That could be expensive with xdg dirs... */
+ if (strcmp (path, g_get_home_dir ()) == 0)
+ type_icon = "user-home";
+ else if (S_ISDIR (statbuf.st_mode))
+ type_icon = "folder";
+ else if (statbuf.st_mode & S_IXUSR)
+ type_icon = "application-x-executable";
+ else
+ type_icon = "text-x-generic";
- generic_mimetype_icon = g_malloc (p - content_type + strlen ("-x-generic") + 1);
- memcpy (generic_mimetype_icon, content_type, p - content_type);
- memcpy (generic_mimetype_icon + (p - content_type), "-x-generic", strlen ("-x-generic"));
- generic_mimetype_icon[(p - content_type) + strlen ("-x-generic")] = 0;
+ g_themed_icon_append_name (G_THEMED_ICON (icon), type_icon);
+ }
- /* TODO: Special case desktop dir? That could be expensive with xdg dirs... */
- if (strcmp (path, g_get_home_dir ()) == 0)
- type_icon = "user-home";
- else if (S_ISDIR (statbuf.st_mode))
- type_icon = "folder";
- else if (statbuf.st_mode & S_IXUSR)
- type_icon = "application-x-executable";
- else
- type_icon = "text-x-generic";
-
- i = 0;
- icon_names[i++] = mimetype_icon;
- icon_names[i++] = generic_mimetype_icon;
- if (strcmp (generic_mimetype_icon, type_icon) != 0 &&
- strcmp (mimetype_icon, type_icon) != 0)
- icon_names[i++] = type_icon;
-
- icon = g_themed_icon_new_from_names (icon_names, i);
- g_file_info_set_icon (info, icon);
-
- g_object_unref (icon);
- g_free (mimetype_icon);
- g_free (generic_mimetype_icon);
+ g_file_info_set_icon (info, icon);
+ g_object_unref (icon);
+ }
}
g_free (content_type);
Index: gio/gthemedicon.c
===================================================================
--- gio/gthemedicon.c (revision 6545)
+++ gio/gthemedicon.c (working copy)
@@ -215,6 +215,28 @@
return (const char * const *)icon->names;
}
+/**
+ * g_themed_icon_append_name:
+ * @icon: a #GThemedIcon
+ * @iconname: name of icon to append to list of icons from within @icon.
+ *
+ * Append a name to the list of icons from within @icon.
+ */
+void
+g_themed_icon_append_name (GThemedIcon *icon, const char *iconname)
+{
+ guint num_names;
+ char **new_names;
+
+ g_return_if_fail (G_IS_THEMED_ICON (icon));
+ g_return_if_fail (iconname != NULL);
+
+ num_names = g_strv_length (icon->names);
+ icon->names = g_realloc (icon->names, sizeof (char*) * (num_names + 2));
+ icon->names[num_names] = g_strdup (iconname);
+ icon->names[num_names + 1] = NULL;
+}
+
static guint
g_themed_icon_hash (GIcon *icon)
{
Index: gio/gthemedicon.h
===================================================================
--- gio/gthemedicon.h (revision 6545)
+++ gio/gthemedicon.h (working copy)
@@ -51,6 +51,7 @@
GIcon *g_themed_icon_new (const char *iconname);
GIcon *g_themed_icon_new_with_default_fallbacks (const char *iconname);
GIcon *g_themed_icon_new_from_names (char **iconnames, int len);
+void g_themed_icon_append_name (GThemedIcon *icon, const char *iconname);
const char * const *g_themed_icon_get_names (GThemedIcon *icon);
Index: gio/ChangeLog
===================================================================
--- gio/ChangeLog (revision 6545)
+++ gio/ChangeLog (working copy)
@@ -1,3 +1,42 @@
+2008-02-21 David Zeuthen <davidz redhat com>
+
+ * glocalfileinfo.c: (_g_local_file_info_get):
+ * gcontenttype.c:
+ (g_content_type_get_icon): Implement this function by
+ moving bits from glocalfileinfo.c
+ (g_content_type_get_description): Unalias before getting
+ description (#517687)
+
+ * gfile.c: (g_file_class_init),
+ (g_file_query_filesystem_info_async),
+ (g_file_query_filesystem_info_finish),
+ (query_filesystem_info_data_free),
+ (query_filesystem_info_async_thread),
+ (g_file_real_query_filesystem_info_async),
+ (g_file_real_query_filesystem_info_finish):
+ * gfile.h: Implement async version of
+ g_file_query_filesystem_info()
+
+ * gfileinfo.h: Add new attributes for filesystem::use-preview
+
+ * gio.symbols: Update
+
+ * gthemedicon.c: (g_themed_icon_append_name):
+ * gthemedicon.h: Add new new convenience function.
+
+ * gunionvolumemonitor.c: (g_union_volume_monitor_dispose),
+ (get_mounts), (get_volumes), (get_connected_drives),
+ (get_volume_for_uuid), (get_mount_for_uuid),
+ (g_union_volume_monitor_init), (populate_union_monitor),
+ (g_volume_monitor_get), (_g_mount_get_for_mount_path),
+ (g_volume_monitor_adopt_orphan_mount):
+ * gvolumemonitor.c:
+ * gvolumemonitor.h: Use recursive locks so it's safe for volume
+ monitor implementations to call into the main volume monitor. Also
+ separate object initialization and volume monitor initialization
+ such that non-native volume monitors can properly adopt their
+ mounts away.
+
2008-02-20 Benjamin Otte <otte gnome org>
* gfile.c: (g_file_mount_mountable), (g_file_unmount_mountable),
Index: gio/gfileinfo.h
===================================================================
--- gio/gfileinfo.h (revision 6545)
+++ gio/gfileinfo.h (working copy)
@@ -80,6 +80,22 @@
G_FILE_TYPE_MOUNTABLE
} GFileType;
+/**
+ * GFilesystemPreviewType:
+ * @G_FILESYSTEM_PREVIEW_TYPE_IF_ALWAYS: Only preview files if user has explicitly requested it.
+ * @G_FILESYSTEM_PREVIEW_TYPE_IF_LOCAL: Preview files if user has requested preview of "local" files.
+ * @G_FILESYSTEM_PREVIEW_TYPE_NEVER: Never preview files.
+ *
+ * Indicates a hint from the file system whether files should be
+ * previewed in a file manager. Returned as the value of the key
+ * #G_FILE_ATTRIBUTE_FILESYSTEM_USE_PREVIEW.
+ **/
+typedef enum {
+ G_FILESYSTEM_PREVIEW_TYPE_IF_ALWAYS = 0,
+ G_FILESYSTEM_PREVIEW_TYPE_IF_LOCAL,
+ G_FILESYSTEM_PREVIEW_TYPE_NEVER
+} GFilesystemPreviewType;
+
/* Common Attributes: */
/**
* G_FILE_ATTRIBUTE_STANDARD_TYPE:
@@ -680,6 +696,16 @@
#define G_FILE_ATTRIBUTE_FILESYSTEM_READONLY "filesystem::readonly" /* boolean */
/**
+ * G_FILE_ATTRIBUTE_FILESYSTEM_USE_PREVIEW:
+ *
+ * A key in the "filesystem" namespace for hinting a file manager
+ * application whether it should preview (e.g. thumbnail) files on the
+ * file system. The value for this key contain a
+ * #GFilesystemPreviewType.
+ **/
+#define G_FILE_ATTRIBUTE_FILESYSTEM_USE_PREVIEW "filesystem::use-preview" /* uint32 (GFilesystemPreviewType) */
+
+/**
* G_FILE_ATTRIBUTE_GVFS_BACKEND:
*
* A key in the "gvfs" namespace that gets the name of the current
Index: gio/gcontenttype.c
===================================================================
--- gio/gcontenttype.c (revision 6545)
+++ gio/gcontenttype.c (working copy)
@@ -1,3 +1,5 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+
/* GIO - GLib Input, Output and Streaming Library
*
* Copyright (C) 2006-2007 Red Hat, Inc.
@@ -25,6 +27,7 @@
#include <string.h>
#include <stdio.h>
#include "gcontenttypeprivate.h"
+#include "gthemedicon.h"
#include "glibintl.h"
#include "gioalias.h"
@@ -591,6 +594,8 @@
g_return_val_if_fail (type != NULL, NULL);
G_LOCK (gio_xdgmime);
+ type = xdg_mime_unalias_mime_type (type);
+
if (type_comment_cache == NULL)
type_comment_cache = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
@@ -639,10 +644,35 @@
GIcon *
g_content_type_get_icon (const char *type)
{
+ char *mimetype_icon, *generic_mimetype_icon, *p;
+ char *icon_names[2];
+ GThemedIcon *themed_icon;
+
g_return_val_if_fail (type != NULL, NULL);
-
- /* TODO: Implement */
- return NULL;
+
+ mimetype_icon = g_strdup (type);
+
+ while ((p = strchr (mimetype_icon, '/')) != NULL)
+ *p = '-';
+
+ p = strchr (type, '/');
+ if (p == NULL)
+ p = type + strlen (type);
+
+ generic_mimetype_icon = g_malloc (p - type + strlen ("-x-generic") + 1);
+ memcpy (generic_mimetype_icon, type, p - type);
+ memcpy (generic_mimetype_icon + (p - type), "-x-generic", strlen ("-x-generic"));
+ generic_mimetype_icon[(p - type) + strlen ("-x-generic")] = 0;
+
+ icon_names[0] = mimetype_icon;
+ icon_names[1] = generic_mimetype_icon;
+
+ themed_icon = g_themed_icon_new_from_names (icon_names, 2);
+
+ g_free (mimetype_icon);
+ g_free (generic_mimetype_icon);
+
+ return G_ICON (themed_icon);
}
/**
Index: gio/gvolumemonitor.c
===================================================================
--- gio/gvolumemonitor.c (revision 6545)
+++ gio/gvolumemonitor.c (working copy)
@@ -1,3 +1,5 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+
/* GIO - GLib Input, Output and Streaming Library
*
* Copyright (C) 2006-2007 Red Hat, Inc.
Index: gio/gvolumemonitor.h
===================================================================
--- gio/gvolumemonitor.h (revision 6545)
+++ gio/gvolumemonitor.h (working copy)
@@ -1,3 +1,5 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+
/* GIO - GLib Input, Output and Streaming Library
*
* Copyright (C) 2006-2007 Red Hat, Inc.
Index: gio/gfile.c
===================================================================
--- gio/gfile.c (revision 6545)
+++ gio/gfile.c (working copy)
@@ -1,3 +1,5 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+
/* GIO - GLib Input, Output and Streaming Library
*
* Copyright (C) 2006-2007 Red Hat, Inc.
@@ -100,6 +102,15 @@
static GFileInfo * g_file_real_query_info_finish (GFile *file,
GAsyncResult *res,
GError **error);
+static void g_file_real_query_filesystem_info_async (GFile *file,
+ const char *attributes,
+ int io_priority,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+static GFileInfo * g_file_real_query_filesystem_info_finish (GFile *file,
+ GAsyncResult *res,
+ GError **error);
static void g_file_real_enumerate_children_async (GFile *file,
const char *attributes,
GFileQueryInfoFlags flags,
@@ -235,6 +246,8 @@
iface->set_display_name_finish = g_file_real_set_display_name_finish;
iface->query_info_async = g_file_real_query_info_async;
iface->query_info_finish = g_file_real_query_info_finish;
+ iface->query_filesystem_info_async = g_file_real_query_filesystem_info_async;
+ iface->query_filesystem_info_finish = g_file_real_query_filesystem_info_finish;
iface->set_attributes_async = g_file_real_set_attributes_async;
iface->set_attributes_finish = g_file_real_set_attributes_finish;
iface->read_async = g_file_real_read_async;
@@ -1081,6 +1094,81 @@
}
/**
+ * g_file_query_filesystem_info_async:
+ * @file: input #GFile.
+ * @attributes: an attribute query string.
+ * @io_priority: the <link linkend="io-priority">I/O priority</link>
+ * of the request.
+ * @cancellable: optional #GCancellable object, %NULL to ignore.
+ * @callback: a #GAsyncReadyCallback to call when the request is satisfied
+ * @user_data: the data to pass to callback function
+ *
+ * Asynchronously gets the requested information about the filesystem
+ * that the specified @file is on. The result is a #GFileInfo object
+ * that contains key-value attributes (such as type or size for the
+ * file).
+ *
+ * For more details, see g_file_query_filesystem_info() which is the
+ * synchronous version of this call.
+ *
+ * When the operation is finished, @callback will be called. You can
+ * then call g_file_query_info_finish() to get the result of the
+ * operation.
+ **/
+void
+g_file_query_filesystem_info_async (GFile *file,
+ const char *attributes,
+ int io_priority,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GFileIface *iface;
+
+ g_return_if_fail (G_IS_FILE (file));
+
+ iface = G_FILE_GET_IFACE (file);
+ (* iface->query_filesystem_info_async) (file,
+ attributes,
+ io_priority,
+ cancellable,
+ callback,
+ user_data);
+}
+
+/**
+ * g_file_query_filesystem_info_finish:
+ * @file: input #GFile.
+ * @res: a #GAsyncResult.
+ * @error: a #GError.
+ *
+ * Finishes an asynchronous filesystem info query. See
+ * g_file_query_filesystem_info_async().
+ *
+ * Returns: #GFileInfo for given @file or %NULL on error.
+ **/
+GFileInfo *
+g_file_query_filesystem_info_finish (GFile *file,
+ GAsyncResult *res,
+ GError **error)
+{
+ GFileIface *iface;
+
+ g_return_val_if_fail (G_IS_FILE (file), NULL);
+ g_return_val_if_fail (G_IS_ASYNC_RESULT (res), NULL);
+
+ if (G_IS_SIMPLE_ASYNC_RESULT (res))
+ {
+ GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (res);
+ if (g_simple_async_result_propagate_error (simple, error))
+ return NULL;
+ }
+
+ iface = G_FILE_GET_IFACE (file);
+ return (* iface->query_filesystem_info_finish) (file, res, error);
+}
+
+/**
* g_file_find_enclosing_mount:
* @file: input #GFile.
* @cancellable: optional #GCancellable object, %NULL to ignore.
@@ -3695,6 +3783,80 @@
typedef struct {
char *attributes;
+ GFileInfo *info;
+} QueryFilesystemInfoAsyncData;
+
+static void
+query_filesystem_info_data_free (QueryFilesystemInfoAsyncData *data)
+{
+ if (data->info)
+ g_object_unref (data->info);
+ g_free (data->attributes);
+ g_free (data);
+}
+
+static void
+query_filesystem_info_async_thread (GSimpleAsyncResult *res,
+ GObject *object,
+ GCancellable *cancellable)
+{
+ GError *error = NULL;
+ QueryFilesystemInfoAsyncData *data;
+ GFileInfo *info;
+
+ data = g_simple_async_result_get_op_res_gpointer (res);
+
+ info = g_file_query_filesystem_info (G_FILE (object), data->attributes, cancellable, &error);
+
+ if (info == NULL)
+ {
+ g_simple_async_result_set_from_error (res, error);
+ g_error_free (error);
+ }
+ else
+ data->info = info;
+}
+
+static void
+g_file_real_query_filesystem_info_async (GFile *file,
+ const char *attributes,
+ int io_priority,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GSimpleAsyncResult *res;
+ QueryFilesystemInfoAsyncData *data;
+
+ data = g_new0 (QueryFilesystemInfoAsyncData, 1);
+ data->attributes = g_strdup (attributes);
+
+ res = g_simple_async_result_new (G_OBJECT (file), callback, user_data, g_file_real_query_filesystem_info_async);
+ g_simple_async_result_set_op_res_gpointer (res, data, (GDestroyNotify)query_filesystem_info_data_free);
+
+ g_simple_async_result_run_in_thread (res, query_filesystem_info_async_thread, io_priority, cancellable);
+ g_object_unref (res);
+}
+
+static GFileInfo *
+g_file_real_query_filesystem_info_finish (GFile *file,
+ GAsyncResult *res,
+ GError **error)
+{
+ GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (res);
+ QueryFilesystemInfoAsyncData *data;
+
+ g_warn_if_fail (g_simple_async_result_get_source_tag (simple) == g_file_real_query_filesystem_info_async);
+
+ data = g_simple_async_result_get_op_res_gpointer (simple);
+ if (data->info)
+ return g_object_ref (data->info);
+
+ return NULL;
+}
+
+typedef struct {
+ char *attributes;
GFileQueryInfoFlags flags;
GFileEnumerator *enumerator;
} EnumerateChildrenAsyncData;
Index: gio/gfile.h
===================================================================
--- gio/gfile.h (revision 6545)
+++ gio/gfile.h (working copy)
@@ -198,8 +198,8 @@
* @query_info_async: Asynchronously gets the #GFileInfo for a #GFile.
* @query_info_finish: Finishes an asynchronous query info operation.
* @query_filesystem_info: Gets a #GFileInfo for the file system #GFile is on.
- * @_query_filesystem_info_async: Asynchronously gets a #GFileInfo for the file system #GFile is on.
- * @_query_filesystem_info_finish: Finishes asynchronously getting the file system info.
+ * @query_filesystem_info_async: Asynchronously gets a #GFileInfo for the file system #GFile is on.
+ * @query_filesystem_info_finish: Finishes asynchronously getting the file system info.
* @find_enclosing_mount: Gets a #GMount for the #GFile.
* @find_enclosing_mount_async: Asynchronously gets the #GMount for a #GFile.
* @find_enclosing_mount_finish: Finishes asynchronously getting the volume.
@@ -324,8 +324,15 @@
const char *attributes,
GCancellable *cancellable,
GError **error);
- void (*_query_filesystem_info_async) (void);
- void (*_query_filesystem_info_finish) (void);
+ void (*query_filesystem_info_async) (GFile *file,
+ const char *attributes,
+ int io_priority,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+ GFileInfo * (*query_filesystem_info_finish) (GFile *file,
+ GAsyncResult *res,
+ GError **error);
GMount * (*find_enclosing_mount)(GFile *file,
GCancellable *cancellable,
@@ -658,6 +665,15 @@
const char *attributes,
GCancellable *cancellable,
GError **error);
+void g_file_query_filesystem_info_async (GFile *file,
+ const char *attributes,
+ int io_priority,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+GFileInfo * g_file_query_filesystem_info_finish (GFile *file,
+ GAsyncResult *res,
+ GError **error);
GMount * g_file_find_enclosing_mount (GFile *file,
GCancellable *cancellable,
GError **error);
Index: gio/gunionvolumemonitor.c
===================================================================
--- gio/gunionvolumemonitor.c (revision 6545)
+++ gio/gunionvolumemonitor.c (working copy)
@@ -1,3 +1,5 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+
/* GIO - GLib Input, Output and Streaming Library
*
* Copyright (C) 2006-2007 Red Hat, Inc.
@@ -51,8 +53,8 @@
#define g_union_volume_monitor_get_type _g_union_volume_monitor_get_type
G_DEFINE_TYPE (GUnionVolumeMonitor, g_union_volume_monitor, G_TYPE_VOLUME_MONITOR);
+static GStaticRecMutex the_volume_monitor_mutex = G_STATIC_REC_MUTEX_INIT;
-G_LOCK_DEFINE_STATIC(the_volume_monitor);
static GUnionVolumeMonitor *the_volume_monitor = NULL;
static void
@@ -82,9 +84,9 @@
monitor = G_UNION_VOLUME_MONITOR (object);
- G_LOCK (the_volume_monitor);
+ g_static_rec_mutex_lock (&the_volume_monitor_mutex);
the_volume_monitor = NULL;
- G_UNLOCK (the_volume_monitor);
+ g_static_rec_mutex_unlock (&the_volume_monitor_mutex);
if (G_OBJECT_CLASS (g_union_volume_monitor_parent_class)->dispose)
(*G_OBJECT_CLASS (g_union_volume_monitor_parent_class)->dispose) (object);
@@ -102,7 +104,7 @@
res = NULL;
- G_LOCK (the_volume_monitor);
+ g_static_rec_mutex_lock (&the_volume_monitor_mutex);
for (l = monitor->monitors; l != NULL; l = l->next)
{
@@ -111,7 +113,7 @@
res = g_list_concat (res, g_volume_monitor_get_mounts (child_monitor));
}
- G_UNLOCK (the_volume_monitor);
+ g_static_rec_mutex_unlock (&the_volume_monitor_mutex);
return res;
}
@@ -128,7 +130,7 @@
res = NULL;
- G_LOCK (the_volume_monitor);
+ g_static_rec_mutex_lock (&the_volume_monitor_mutex);
for (l = monitor->monitors; l != NULL; l = l->next)
{
@@ -137,7 +139,7 @@
res = g_list_concat (res, g_volume_monitor_get_volumes (child_monitor));
}
- G_UNLOCK (the_volume_monitor);
+ g_static_rec_mutex_unlock (&the_volume_monitor_mutex);
return res;
}
@@ -154,7 +156,7 @@
res = NULL;
- G_LOCK (the_volume_monitor);
+ g_static_rec_mutex_lock (&the_volume_monitor_mutex);
for (l = monitor->monitors; l != NULL; l = l->next)
{
@@ -163,7 +165,7 @@
res = g_list_concat (res, g_volume_monitor_get_connected_drives (child_monitor));
}
- G_UNLOCK (the_volume_monitor);
+ g_static_rec_mutex_unlock (&the_volume_monitor_mutex);
return res;
}
@@ -180,7 +182,7 @@
volume = NULL;
- G_LOCK (the_volume_monitor);
+ g_static_rec_mutex_lock (&the_volume_monitor_mutex);
for (l = monitor->monitors; l != NULL; l = l->next)
{
@@ -192,7 +194,7 @@
}
- G_UNLOCK (the_volume_monitor);
+ g_static_rec_mutex_unlock (&the_volume_monitor_mutex);
return volume;
}
@@ -209,7 +211,7 @@
mount = NULL;
- G_LOCK (the_volume_monitor);
+ g_static_rec_mutex_lock (&the_volume_monitor_mutex);
for (l = monitor->monitors; l != NULL; l = l->next)
{
@@ -221,7 +223,7 @@
}
- G_UNLOCK (the_volume_monitor);
+ g_static_rec_mutex_unlock (&the_volume_monitor_mutex);
return mount;
}
@@ -470,6 +472,11 @@
static void
g_union_volume_monitor_init (GUnionVolumeMonitor *union_monitor)
{
+}
+
+static void
+populate_union_monitor (GUnionVolumeMonitor *union_monitor)
+{
GVolumeMonitor *monitor;
GNativeVolumeMonitorClass *native_class;
GVolumeMonitorClass *klass;
@@ -526,17 +533,18 @@
{
GVolumeMonitor *vm;
- G_LOCK (the_volume_monitor);
+ g_static_rec_mutex_lock (&the_volume_monitor_mutex);
if (the_volume_monitor)
vm = G_VOLUME_MONITOR (g_object_ref (the_volume_monitor));
else
{
the_volume_monitor = g_union_volume_monitor_new ();
+ populate_union_monitor (the_volume_monitor);
vm = G_VOLUME_MONITOR (the_volume_monitor);
}
- G_UNLOCK (the_volume_monitor);
+ g_static_rec_mutex_unlock (&the_volume_monitor_mutex);
return vm;
}
@@ -563,9 +571,9 @@
if (klass->get_mount_for_mount_path)
{
- G_LOCK (the_volume_monitor);
+ g_static_rec_mutex_lock (&the_volume_monitor_mutex);
mount = klass->get_mount_for_mount_path (mount_path, cancellable);
- G_UNLOCK (the_volume_monitor);
+ g_static_rec_mutex_unlock (&the_volume_monitor_mutex);
}
/* TODO: How do we know this succeeded? Keep in mind that the native
@@ -632,10 +640,8 @@
volume = NULL;
- /* TODO: nasty locking issues because current VM's don't emit signals in idle */
+ g_static_rec_mutex_lock (&the_volume_monitor_mutex);
- /*G_LOCK (the_volume_monitor);*/
-
for (l = the_volume_monitor->monitors; l != NULL; l = l->next)
{
child_monitor = l->data;
@@ -649,7 +655,7 @@
}
}
- /*G_UNLOCK (the_volume_monitor);*/
+ g_static_rec_mutex_unlock (&the_volume_monitor_mutex);
return volume;
}
Index: gio/gio.symbols
===================================================================
--- gio/gio.symbols (revision 6545)
+++ gio/gio.symbols (working copy)
@@ -253,6 +253,8 @@
g_file_query_info_async
g_file_query_info_finish
g_file_query_filesystem_info
+g_file_query_filesystem_info_async
+g_file_query_filesystem_info_finish
g_file_find_enclosing_mount
g_file_find_enclosing_mount_async
g_file_find_enclosing_mount_finish
@@ -627,6 +629,7 @@
g_themed_icon_new_with_default_fallbacks
g_themed_icon_new_from_names
g_themed_icon_get_names
+g_themed_icon_append_name
#endif
#endif
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]