[gnome-photos/wip/rishi/thumbnailer: 9/12] Add an out-of-process custom thumbnailer



commit 4f941a42dc5b2f51578789e186c7fdcde4aad619
Author: Debarshi Ray <debarshir gnome org>
Date:   Thu Feb 9 20:24:37 2017 +0100

    Add an out-of-process custom thumbnailer

 src/Makefile.am                 |   49 +++
 src/photos-thumbnail.c          |   59 ++++
 src/photos-thumbnail.h          |   32 ++
 src/photos-thumbnailer-dbus.xml |   37 +++
 src/photos-thumbnailer-main.c   |   54 ++++
 src/photos-thumbnailer.c        |  665 +++++++++++++++++++++++++++++++++++++++
 src/photos-thumbnailer.h        |   35 ++
 7 files changed, 931 insertions(+), 0 deletions(-)
---
diff --git a/src/Makefile.am b/src/Makefile.am
index b33cf61..ba1e4bc 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -1,5 +1,7 @@
 bin_PROGRAMS = gnome-photos
 
+libexec_PROGRAMS = gnome-photos-thumbnailer
+
 gnome_photos_built_sources = \
        photos-about-data.c \
        photos-about-data.h \
@@ -265,8 +267,34 @@ gnome_photos_SOURCES = \
        photos-main.c \
        $(NULL)
 
+gnome_photos_thumbnailer_built_sources = \
+       photos-thumbnailer-dbus.c \
+       photos-thumbnailer-dbus.h \
+       $(NULL)
+
+nodist_gnome_photos_thumbnailer_SOURCES = \
+       $(gnome_photos_thumbnailer_built_sources) \
+       $(NULL)
+
+gnome_photos_thumbnailer_SOURCES = \
+       photos-debug.c \
+       photos-debug.h \
+       photos-error.c \
+       photos-error.h \
+       photos-gegl.c \
+       photos-gegl.h \
+       photos-quarks.c \
+       photos-quarks.h \
+       photos-thumbnail.c \
+       photos-thumbnail.h \
+       photos-thumbnailer.c \
+       photos-thumbnailer.h \
+       photos-thumbnailer-main.c \
+       $(NULL)
+
 BUILT_SOURCES = \
        $(gnome_photos_built_sources) \
+       $(gnome_photos_thumbnailer_built_sources) \
        $(NULL)
 
 EXTRA_DIST = \
@@ -285,6 +313,7 @@ EXTRA_DIST = \
        photos-selection-toolbar.ui \
        photos-share-dialog.ui \
        photos-gom-miner.xml \
+       photos-thumbnailer-dbus.xml \
        photos-tracker-extract-priority.xml \
        photos-tracker-resources.xml \
        photos-dleyna-renderer-device.xml \
@@ -347,6 +376,18 @@ gnome_photos_LDADD = \
        $(top_builddir)/libgd/libgd.la \
        $(NULL)
 
+gnome_photos_thumbnailer_LDFLAGS = \
+       $(WARN_LDFLAGS) \
+       $(NULL)
+
+gnome_photos_thumbnailer_LDADD = \
+       $(BABL_LIBS) \
+       $(GEGL_LIBS) \
+       $(GDK_PIXBUF_LIBS) \
+       $(GIO_LIBS) \
+       $(GLIB_LIBS) \
+       $(NULL)
+
 CLEANFILES = \
        $(BUILT_SOURCES) \
        stamp-photos-about-data.h \
@@ -493,6 +534,14 @@ photos-shell-search-provider2.h photos-shell-search-provider2.c: org.gnome.Shell
                --interface-prefix org.gnome.Shell. \
                $<
 
+photos-thumnailer-dbus.h photos-thumbnailer-dbus.c: photos-thumbnailer-dbus.xml
+       $(AM_V_GEN)gdbus-codegen \
+               --c-namespace Photos \
+               --generate-c-code photos-thumbnailer-dbus \
+               --interface-prefix org.gnome.Photos. \
+               --annotate "org.gnome.Photos.Thumbnailer" org.gtk.GDBus.C.Name ThumbnailerDBus \
+               $<
+
 photos-tracker-extract-priority.h photos-tracker-extract-priority.c: photos-tracker-extract-priority.xml
        $(AM_V_GEN)gdbus-codegen \
                --c-namespace Tracker \
diff --git a/src/photos-thumbnail.c b/src/photos-thumbnail.c
new file mode 100644
index 0000000..0baeada
--- /dev/null
+++ b/src/photos-thumbnail.c
@@ -0,0 +1,59 @@
+/*
+ * Photos - access, organize and share your photos on GNOME
+ * Copyright © 2017 Red Hat, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+
+#include "config.h"
+
+#include "photos-thumbnail.h"
+
+
+enum
+{
+  THUMBNAIL_GENERATION = 0
+};
+
+
+gchar *
+photos_thumbnail_path_for_uri (const gchar *uri, gint size)
+{
+  const gchar *cache_dir;
+  gchar *filename = NULL;
+  gchar *md5 = NULL;
+  gchar *path;
+  gchar *thumbnails_subdir = NULL;
+
+  md5 = g_compute_checksum_for_string (G_CHECKSUM_MD5, uri, -1);
+  filename = g_strconcat (md5, ".png", NULL);
+
+  cache_dir = g_get_user_cache_dir ();
+  thumbnails_subdir = g_strdup_printf ("%d-%d", size, THUMBNAIL_GENERATION);
+
+  path = g_build_filename (cache_dir,
+                           PACKAGE_TARNAME,
+                           "thumbnails",
+                           thumbnails_subdir,
+                           filename,
+                           NULL);
+
+  g_free (filename);
+  g_free (md5);
+  g_free (thumbnails_subdir);
+  return path;
+}
diff --git a/src/photos-thumbnail.h b/src/photos-thumbnail.h
new file mode 100644
index 0000000..eb0b4f1
--- /dev/null
+++ b/src/photos-thumbnail.h
@@ -0,0 +1,32 @@
+/*
+ * Photos - access, organize and share your photos on GNOME
+ * Copyright © 2017 Red Hat, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+#ifndef PHOTOS_THUMBNAIL_H
+#define PHOTOS_THUMBNAIL_H
+
+#include <glib.h>
+
+G_BEGIN_DECLS
+
+gchar           *photos_thumbnail_path_for_uri      (const gchar *uri, gint size);
+
+G_END_DECLS
+
+#endif /* PHOTOS_THUMBNAIL_H */
diff --git a/src/photos-thumbnailer-dbus.xml b/src/photos-thumbnailer-dbus.xml
new file mode 100644
index 0000000..452c0d2
--- /dev/null
+++ b/src/photos-thumbnailer-dbus.xml
@@ -0,0 +1,37 @@
+<!DOCTYPE node PUBLIC
+"-//freedesktop//DTD D-BUS Object Introspection 1.0//EN"
+"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd";>
+
+<!--
+ Photos - access, organize and share your photos on GNOME
+ Copyright © 2017 Red Hat, Inc.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation; either version 2
+ of the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ 02110-1301, USA.
+-->
+
+<node name="/" xmlns:doc="http://www.freedesktop.org/dbus/1.0/doc.dtd";>
+  <interface name="org.gnome.Photos.Thumbnailer">
+    <method name="GenerateThumbnail">
+      <arg name="uri" type="s" direction="in" />
+      <arg name="mime_type" type="s" direction="in" />
+      <arg name="orientation" type="s" direction="in" />
+      <arg name="original_height" type="x" direction="in" />
+      <arg name="original_width" type="x" direction="in" />
+      <arg name="pipeline_uri" type="s" direction="in" />
+      <arg name="thumbnail_size" type="i" direction="in" />
+    </method>
+  </interface>
+</node>
diff --git a/src/photos-thumbnailer-main.c b/src/photos-thumbnailer-main.c
new file mode 100644
index 0000000..1f9c517
--- /dev/null
+++ b/src/photos-thumbnailer-main.c
@@ -0,0 +1,54 @@
+/*
+ * Photos - access, organize and share your photos on GNOME
+ * Copyright © 2017 Red Hat, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+
+#include "config.h"
+
+#include <locale.h>
+
+#include <glib.h>
+#include <glib/gi18n.h>
+
+#include "photos-debug.h"
+#include "photos-thumbnailer.h"
+
+
+gint
+main (gint argc, gchar *argv[])
+{
+  GApplication *app;
+  gint exit_status = 0;
+
+  setlocale (LC_ALL, "");
+
+  photos_debug_init ();
+
+  bindtextdomain (GETTEXT_PACKAGE, PACKAGE_LOCALE_DIR);
+  bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
+  textdomain (GETTEXT_PACKAGE);
+
+  g_set_prgname (PACKAGE_TARNAME "-thumbnailer");
+
+  app = photos_thumbnailer_new ();
+  exit_status = g_application_run (app, argc, argv);
+
+  g_object_unref (app);
+  return exit_status;
+}
diff --git a/src/photos-thumbnailer.c b/src/photos-thumbnailer.c
new file mode 100644
index 0000000..3e68d75
--- /dev/null
+++ b/src/photos-thumbnailer.c
@@ -0,0 +1,665 @@
+/*
+ * Photos - access, organize and share your photos on GNOME
+ * Copyright © 2017 Red Hat, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+
+#include "config.h"
+
+#include <stdlib.h>
+
+#include <babl/babl.h>
+#include <gdk-pixbuf/gdk-pixbuf.h>
+#include <gegl.h>
+#include <glib/gi18n.h>
+
+#include "photos-debug.h"
+#include "photos-error.h"
+#include "photos-gegl.h"
+#include "photos-thumbnailer.h"
+#include "photos-thumbnailer-dbus.h"
+
+
+struct _PhotosThumbnailer
+{
+  GApplication parent_instance;
+  GDBusConnection *connection;
+  PhotosThumbnailerDBus *skeleton;
+  gchar *address;
+};
+
+
+G_DEFINE_TYPE (PhotosThumbnailer, photos_thumbnailer, G_TYPE_APPLICATION);
+
+
+typedef struct _PhotosThumbnailerGenerateData PhotosThumbnailerGenerateData;
+
+struct _PhotosThumbnailerGenerateData
+{
+  GFile *file;
+  GQuark orientation;
+  GdkPixbuf *pixbuf_thumbnail;
+  GeglNode *graph;
+  gint thumbnail_size;
+  gint64 original_height;
+  gint64 original_width;
+};
+
+enum
+{
+  INACTIVITY_TIMEOUT = 12000 /* ms */
+};
+
+static const GOptionEntry COMMAND_LINE_OPTIONS[] =
+{
+  { "address", 0, G_OPTION_FLAG_NONE, G_OPTION_ARG_STRING, NULL, N_("D-Bus address to use"), NULL},
+  { NULL }
+};
+
+static const gchar *THUMBNAILER_PATH = "/org/gnome/Photos/Thumbnailer";
+
+
+static PhotosThumbnailerGenerateData *
+photos_thumbnailer_generate_data_new (GFile *file,
+                                      GQuark orientation,
+                                      gint64 original_height,
+                                      gint64 original_width,
+                                      gint thumbnail_size,
+                                      GeglNode *graph)
+{
+  PhotosThumbnailerGenerateData *data;
+
+  data = g_slice_new0 (PhotosThumbnailerGenerateData);
+  data->file = g_object_ref (file);
+  data->orientation = orientation;
+  data->thumbnail_size = thumbnail_size;
+  data->original_height = original_height;
+  data->original_width = original_width;
+  data->graph = g_object_ref (graph);
+
+  return data;
+}
+
+
+static void
+photos_thumbnailer_generate_data_free (PhotosThumbnailerGenerateData *data)
+{
+  g_object_unref (data->file);
+  g_object_unref (data->graph);
+  g_object_unref (data->pixbuf_thumbnail);
+  g_slice_free (PhotosThumbnailerGenerateData, data);
+}
+
+
+static gboolean
+photos_thumbnailer_authorize_authenticated_peer (PhotosThumbnailer *self,
+                                                 GIOStream *iostream,
+                                                 GCredentials *credentials)
+{
+  GCredentials *own_credentials = NULL;
+  GError *error;
+  gboolean ret_val = FALSE;
+
+  if (credentials == NULL)
+    goto out;
+
+  own_credentials = g_credentials_new ();
+
+  error = NULL;
+  if (!g_credentials_is_same_user (credentials, own_credentials, &error))
+    {
+      g_warning ("Unable to authorize peer: %s", error->message);
+      g_error_free (error);
+      goto out;
+    }
+
+  ret_val = TRUE;
+
+ out:
+  g_clear_object (&own_credentials);
+  return ret_val;
+}
+
+
+static void
+photos_thumbnailer_generate_thumbnail_in_thread_func (GTask *task,
+                                                      gpointer source_object,
+                                                      gpointer task_data,
+                                                      GCancellable *cancellable)
+{
+  const Babl *format;
+  GeglBuffer *buffer_orig = NULL;
+  GeglBuffer *buffer_zoomed = NULL;
+  GeglRectangle bbox;
+  GdkPixbuf *pixbuf = NULL;
+  PhotosThumbnailerGenerateData *data;
+  gdouble zoom;
+  gint bpp;
+  gint min_dimension;
+  guchar *buf = NULL;
+
+  data = (PhotosThumbnailerGenerateData *) g_task_get_task_data (task);
+
+  {
+    GeglNode *buffer_sink;
+    GeglNode *graph = NULL;
+    GeglNode *load;
+    gchar *path;
+
+    path = g_file_get_path (data->file);
+
+    graph = gegl_node_new ();
+    load = gegl_node_new_child (graph, "operation", "gegl:load", "path", path, NULL);
+    buffer_sink = gegl_node_new_child (graph, "operation", "gegl:buffer-sink", "buffer", &buffer_orig, NULL);
+    gegl_node_link_many (load, buffer_sink, NULL);
+    gegl_node_process (buffer_sink);
+
+    g_object_unref (graph);
+    g_free (path);
+  }
+
+  bbox = *gegl_buffer_get_extent (buffer_orig);
+  min_dimension = MIN (bbox.height, bbox.width);
+  zoom = (gdouble) data->size / (gdouble) min_dimension;
+
+  bbox.height = (gint) ((gdouble) bbox.height * zoom + 0.5);
+  bbox.width = (gint) ((gdouble) bbox.width * zoom + 0.5);
+  bbox.x = (gint) ((gdouble) bbox.x * zoom + 0.5);
+  bbox.y = (gint) ((gdouble) bbox.y * zoom + 0.5);
+
+  format = gegl_buffer_get_format (buffer_orig);
+  bpp = babl_format_get_bytes_per_pixel (format);
+  buf = g_malloc0_n (bbox.height * bbox.width, bpp);
+
+  gegl_buffer_get (buffer_orig, &bbox, zoom, format, buf, GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
+
+  bbox.x = 0;
+  bbox.y = 0;
+  buffer_zoomed = gegl_buffer_linear_new_from_data (buf, format, &bbox, GEGL_AUTO_ROWSTRIDE, g_free, NULL);
+
+  {
+    GeglNode *buffer_source;
+    GeglNode *graph = NULL;
+    GeglNode *orientation;
+    GeglNode *save_pixbuf;
+
+    graph = gegl_node_new ();
+    buffer_source = gegl_node_new_child (graph, "operation", "gegl:buffer-source", "buffer", buffer_zoomed, 
NULL);
+    orientation = photos_gegl_create_orientation_node (graph, data->orientation);
+    save_pixbuf = gegl_node_new_child (graph, "operation", "gegl:save-pixbuf", "pixbuf", &pixbuf, NULL);
+    gegl_node_link_many (buffer_source, orientation, save_pixbuf, NULL);
+    gegl_node_process (save_pixbuf);
+
+    g_object_unref (graph);
+  }
+
+  g_clear_object (&buffer_orig);
+  g_clear_object (&buffer_zoomed);
+  g_clear_object (&pixbuf);
+}
+
+
+static void
+photos_thumbnailer_generate_thumbnail_process (GObject *source_object, GAsyncResult *res, gpointer user_data)
+{
+  GTask *task = G_TASK (user_data);
+  GCancellable *cancellable;
+  GError *error;
+  GFile *file = NULL;
+  GeglProcessor *processor = GEGL_PROCESSOR (source_object);
+  PhotosThumbnailerGenerateData *data;
+  gchar *path = NULL;
+
+  self = PHOTOS_THUMBNAILER (g_task_get_source_object (task));
+  cancellable = g_task_get_cancellable (task);
+  data = g_task_get_task_data (task);
+
+  error = NULL;
+  if (!photos_gegl_processor_process_finish (processor, res, &error))
+    {
+      g_task_return_error (task, error);
+      goto out;
+    }
+
+  path = photos_thumbnail_path_for_file (data->file);
+  file = g_file_new_for_path (path);
+  g_file_replace_async (file,
+                        NULL,
+                        FALSE,
+                        G_FILE_CREATE_REPLACE_DESTINATION,
+                        G_PRIORITY_DEFAULT,
+                        cancellable,
+                        photos_thumbnailer_generate_thumbnail_replace,
+                        g_object_ref (task));
+
+  g_task_run_in_thread (task, photos_thumbnailer_generate_thumbnail_in_thread_func);
+
+ out:
+  g_free (path);
+  g_clear_object (&file);
+  g_object_unref (task);
+}
+
+
+static void
+photos_thumbnailer_generate_thumbnail_pixbuf (GObject *source_object, GAsyncResult *res, gpointer user_data)
+{
+  GTask *task = G_TASK (user_data);
+  GCancellable *cancellable;
+  GError *error;
+  GdkPixbuf *pixbuf = NULL;
+  GdkPixbuf *pixbuf_oriented = NULL;
+  GeglNode *pipeline_node;
+  GeglNode *pixbuf_source;
+  GeglNode *save_pixbuf;
+  GeglProcessor *processor = NULL;
+  PhotosThumbnailerGenerateData *data;
+
+  self = PHOTOS_THUMBNAILER (g_task_get_source_object (task));
+  cancellable = g_task_get_cancellable (task);
+  data = g_task_get_task_data (task);
+
+  error = NULL;
+  pixbuf = photos_pixbuf_new_from_file_finish (res, &error);
+  if (error != NULL)
+    {
+      g_task_return_error (task, error);
+      goto out;
+    }
+
+  pixbuf_oriented = gdk_pixbuf_apply_embedded_orientation (pixbuf);
+
+  g_assert_null (data->pixbuf_thumbnail);
+
+  pixbuf_source = gegl_node_new_child (data->graph, "operation", "gegl:pixbuf", pixbuf_oriented, NULL);
+  pipeline_node = photos_pipeline_get_graph (data->pipeline);
+  save_pixbuf = gegl_node_new_child (data->graph,
+                                     "operation", "gegl:save-pixbuf",
+                                     "pixbuf", &data->pixbuf_thumbnail,
+                                     NULL);
+
+  gegl_node_link_many (pixbuf_source, pipeline_node, save_pixbuf, NULL);
+
+  processor = photos_pipeline_new_processor (data->pipeline);
+  photos_gegl_processor_process_async (processor,
+                                       cancellable,
+                                       photos_thumbnailer_generate_thumbnail_process,
+                                       g_object_ref (task));
+
+ out:
+  g_clear_object (&pixbuf);
+  g_clear_object (&pixbuf_oriented);
+  g_clear_object (&processor);
+  g_object_unref (task);
+}
+
+
+static void
+photos_thumbnailer_generate_thumbnail_pipeline (GObject *source_object, GAsyncResult *res, gpointer 
user_data)
+{
+  GTask *task = G_TASK (user_data);
+  GError *error;
+  PhotosPipeline *pipeline = NULL;
+  PhotosThumbnailerGenerateData *data;
+  gchar *path = NULL;
+  gdouble height;
+  gdouble width;
+  gdouble x;
+  gdouble y;
+  gint load_height;
+  gint load_width;
+
+  self = PHOTOS_THUMBNAILER (g_task_get_source_object (task));
+  data = g_task_get_task_data (task);
+
+  error = NULL;
+  pipeline = photos_pipeline_new_finish (res, &error);
+  if (error != NULL)
+    {
+      g_task_return_error (task, error);
+      goto out;
+    }
+
+  g_assert_null (data->pipeline);
+  data->pipeline = g_object_ref (pipeline);
+
+  if (photos_pipeline_get (pipeline, "gegl:crop", "height", &height, "width", &width, "x", &x, "y", &y, 
NULL))
+    {
+      g_assert_cmpfloat (height, >=, 0.0);
+      g_assert_cmpfloat (width >= 0.0);
+      g_assert_cmpfloat (x, >=, 0.0);
+      g_assert_cmpfloat (y, >=, 0.0);
+
+      load_height = (gint) (height / (gdouble) data->thumbnail_size * (gdouble) data->original_height + 0.5);
+      load_width = (gint) (width / (gdouble) data->thumbnail_size * (gdouble) data->original_width + 0.5);
+    }
+  else
+    {
+      load_height = data->thumbnail_size;
+      load_width = data->thumbnail_size;
+    }
+
+  path = g_file_get_path (data->file);
+  photos_pixbuf_new_from_file_at_size_async (path,
+                                             load_width,
+                                             load_height,
+                                             photos_thumbnailer_generate_thumbnail_pixbuf,
+                                             g_object_ref (task));
+
+ out:
+  g_free (path);
+  g_clear_object (&pipeline);
+  g_object_unref (task);
+}
+
+
+static void
+photos_thumbnailer_generate_thumbnail_async (PhotosThumbnailer *self,
+                                             const gchar *uri,
+                                             const gchar *mime_type,
+                                             const gchar *orientation,
+                                             gint64 original_height,
+                                             gint64 original_width,
+                                             const gchar *pipeline_uri,
+                                             gint thumbnail_size,
+                                             GCancellable *cancellable,
+                                             GAsyncReadyCallback callback,
+                                             gpointer user_data)
+{
+  GFile *file = NULL;
+  GTask *task = NULL;
+  GQuark orientation_quark;
+  GeglNode *graph;
+  PhotosThumbnailerGenerateData *data;
+
+  g_return_val_if_fail (PHOTOS_IS_THUMBNAILER (self), FALSE);
+  g_return_val_if_fail (uri != NULL && uri[0] != '\0', FALSE);
+  g_return_val_if_fail (mime_type != NULL && mime_type[0] != '\0', FALSE);
+  g_return_val_if_fail (orientation != NULL && orientation[0] != '\0', FALSE);
+  g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), FALSE);
+
+  file = g_file_new_for_uri (uri);
+  orientation_quark = g_quark_from_string (orientation);
+  graph = gegl_node_new ();
+  data = photos_thumbnailer_generate_data_new (file,
+                                               orientation_quark,
+                                               original_height,
+                                               original_width,
+                                               thumbnail_size,
+                                               graph);
+
+  task = g_task_new (self, cancellable, callback, user_data);
+  g_task_set_source_tag (task, photos_thumbnailer_generate_thumbnail_async);
+  g_task_set_task_data (task, data, (GDestroyNotify) photos_thumbnailer_generate_data_free);
+
+  photos_pipeline_new_async (graph,
+                             pipeline_uri,
+                             cancellable,
+                             photos_thumbnailer_generate_thumbnail_pipeline,
+                             g_object_ref (task));
+
+  g_object_unref (file);
+  g_object_unref (graph);
+  g_object_unref (task);
+  return TRUE;
+}
+
+
+static gboolean
+photos_thumbnailer_generate_thumbnail_finish (PhotosThumbnailer *self, GAsyncResult *res, GError **error)
+{
+  GTask *task = G_TASK (res);
+
+  g_return_val_if_fail (g_task_is_valid (res, self), FALSE);
+  g_return_val_if_fail (g_task_get_source_tag (task) == photos_thumbnailer_generate_thumbnail_async, FALSE);
+  g_return_val_if_fail (error == NULL || *error == NULL, NULL);
+
+  return g_task_propagate_boolean (task, error);
+}
+
+
+static void
+photos_thumbnailer_handle_generate_thumbnail_generate_thumbnail (GObject *source_object,
+                                                                 GAsyncResult *res,
+                                                                 gpointer user_data)
+{
+  PhotosThumbnailer *self = PHOTOS_THUMBNAILER (source_object);
+  GDBusMethodInvocation *invocation = G_DBUS_METHOD_INVOCATION (user_data);
+  GError *error;
+
+  error = NULL;
+  if (!photos_thumbnailer_generate_thumbnail_finish (self, res, &error))
+    {
+      g_dbus_method_invocation_take_error (invocation, error);
+      goto out;
+    }
+
+  photos_thumbnailer_dbus_complete_generate_thumbnail (self->skeleton, invocation);
+
+ out:
+  photos_debug (PHOTOS_DEBUG_THUMBNAILER, "Completed GenerateThumbnail");
+  g_application_release (G_APPLICATION (self));
+  g_object_unref (invocation);
+}
+
+
+static gboolean
+photos_thumbnailer_handle_generate_thumbnail (PhotosThumbnailer *self,
+                                              GDBusMethodInvocation *invocation,
+                                              const gchar *uri,
+                                              const gchar *mime_type,
+                                              const gchar *orientation,
+                                              gint64 original_height,
+                                              gint64 original_width,
+                                              const gchar *pipeline_uri,
+                                              gint thumbnail_size)
+{
+  g_return_val_if_fail (PHOTOS_IS_THUMBNAILER (self), FALSE);
+  g_return_val_if_fail (G_IS_DBUS_INVOCATION (invocation), FALSE);
+  g_return_val_if_fail (uri != NULL && uri[0] != '\0', FALSE);
+  g_return_val_if_fail (mime_type != NULL && mime_type[0] != '\0', FALSE);
+  g_return_val_if_fail (orientation != NULL && orientation[0] != '\0', FALSE);
+  g_return_val_if_fail (pipeline_uri != NULL && pipeline_uri[0] != '\0', FALSE);
+
+  photos_debug (PHOTOS_DEBUG_THUMBNAILER, "Handling GenerateThumbnail for %s", uri);
+
+  g_application_hold (G_APPLICATION (self));
+  photos_thumbnailer_generate_thumbnail_async (self,
+                                               uri,
+                                               mime_type,
+                                               orientation,
+                                               original_height,
+                                               original_width,
+                                               pipeline_uri,
+                                               thumbnail_size,
+                                               NULL,
+                                               
photos_thumbnailer_handle_generate_thumbnail_generate_thumbnail,
+                                               g_object_ref (invocation));
+
+  g_clear_object (&file);
+  g_clear_object (&task);
+  return TRUE;
+}
+
+
+static gboolean
+photos_thumbnailer_dbus_register (GApplication *application,
+                                  GDBusConnection *connection,
+                                  const gchar *object_path,
+                                  GError **error)
+{
+  PhotosThumbnailer *self = PHOTOS_THUMBNAILER (application);
+  GDBusAuthObserver *observer = NULL;
+  gboolean ret_val = FALSE;
+
+  if (!G_APPLICATION_CLASS (photos_thumbnailer_parent_class)->dbus_register (application,
+                                                                             connection,
+                                                                             object_path,
+                                                                             error))
+    goto out;
+
+  observer = g_dbus_auth_observer_new ();
+  g_signal_connect_swapped (observer,
+                            "authorize-authenticated-peer",
+                            G_CALLBACK (photos_thumbnailer_authorize_authenticated_peer),
+                            self);
+
+  self->connection = g_dbus_connection_new_for_address_sync (self->address,
+                                                             G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_CLIENT,
+                                                             observer,
+                                                             NULL,
+                                                             error);
+  if (self->connection == NULL)
+    goto out;
+
+  g_message ("exporting skeleton");
+  if (!g_dbus_interface_skeleton_export (G_DBUS_INTERFACE_SKELETON (self->skeleton),
+                                         self->connection,
+                                         THUMBNAILER_PATH,
+                                         error))
+    {
+      g_clear_object (&self->skeleton);
+      goto out;
+    }
+
+  g_message ("dbus register: %s %s", g_dbus_connection_get_unique_name (self->connection), object_path);
+
+  ret_val = TRUE;
+
+ out:
+  g_clear_object (&observer);
+  return ret_val;
+}
+
+
+static void
+photos_thumbnailer_dbus_unregister (GApplication *application,
+                                    GDBusConnection *connection,
+                                    const gchar *object_path)
+{
+  PhotosThumbnailer *self = PHOTOS_THUMBNAILER (application);
+
+  g_message ("dbus unregister: %li", g_get_monotonic_time ());
+
+  if (self->skeleton != NULL)
+    {
+      g_dbus_interface_skeleton_unexport_from_connection (G_DBUS_INTERFACE_SKELETON (self->skeleton),
+                                                          self->connection);
+      g_clear_object (&self->skeleton);
+    }
+
+  g_message ("dbus unregister: chain up");
+  G_APPLICATION_CLASS (photos_thumbnailer_parent_class)->dbus_unregister (application, connection, 
object_path);
+}
+
+
+static gint
+photos_thumbnailer_handle_local_options (GApplication *application, GVariantDict *options)
+{
+  PhotosThumbnailer *self = PHOTOS_THUMBNAILER (application);
+  gint ret_val = EXIT_FAILURE;
+
+  if (g_variant_dict_lookup (options, "address", "s", &self->address))
+    ret_val = -1;
+
+  return ret_val;
+}
+
+
+static void
+photos_thumbnailer_shutdown (GApplication *application)
+{
+  photos_debug (PHOTOS_DEBUG_THUMBNAILER, "Thumbnailer exiting");
+
+  G_APPLICATION_CLASS (photos_thumbnailer_parent_class)->shutdown (application);
+}
+
+
+static void
+photos_thumbnailer_startup (GApplication *application)
+{
+  G_APPLICATION_CLASS (photos_thumbnailer_parent_class)->startup (application);
+
+  gegl_init (NULL, NULL);
+  photos_debug (PHOTOS_DEBUG_THUMBNAILER, "Thumbnailer ready");
+}
+
+
+static void
+photos_thumbnailer_dispose (GObject *object)
+{
+  PhotosThumbnailer *self = PHOTOS_THUMBNAILER (object);
+
+  g_clear_object (&self->connection);
+  g_clear_object (&self->skeleton);
+
+  G_OBJECT_CLASS (photos_thumbnailer_parent_class)->dispose (object);
+}
+
+
+static void
+photos_thumbnailer_finalize (GObject *object)
+{
+  PhotosThumbnailer *self = PHOTOS_THUMBNAILER (object);
+
+  g_free (self->address);
+
+  if (g_application_get_is_registered (G_APPLICATION (self)))
+    gegl_exit ();
+
+  G_OBJECT_CLASS (photos_thumbnailer_parent_class)->finalize (object);
+}
+
+
+static void
+photos_thumbnailer_init (PhotosThumbnailer *self)
+{
+  self->skeleton = photos_thumbnailer_dbus_skeleton_new ();
+  g_signal_connect_swapped (self->skeleton,
+                            "handle-generate-thumbnail",
+                            G_CALLBACK (photos_thumbnailer_handle_generate_thumbnail),
+                            self);
+
+  g_application_add_main_option_entries (G_APPLICATION (self), COMMAND_LINE_OPTIONS);
+}
+
+
+static void
+photos_thumbnailer_class_init (PhotosThumbnailerClass *class)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (class);
+  GApplicationClass *application_class = G_APPLICATION_CLASS (class);
+
+  object_class->dispose = photos_thumbnailer_dispose;
+  object_class->finalize = photos_thumbnailer_finalize;
+  application_class->dbus_register = photos_thumbnailer_dbus_register;
+  application_class->dbus_unregister = photos_thumbnailer_dbus_unregister;
+  application_class->handle_local_options = photos_thumbnailer_handle_local_options;
+  application_class->shutdown = photos_thumbnailer_shutdown;
+  application_class->startup = photos_thumbnailer_startup;
+}
+
+
+GApplication *
+photos_thumbnailer_new (void)
+{
+  return g_object_new (PHOTOS_TYPE_THUMBNAILER,
+                       "flags", G_APPLICATION_IS_SERVICE | G_APPLICATION_NON_UNIQUE,
+                       "inactivity-timeout", INACTIVITY_TIMEOUT,
+                       NULL);
+}
diff --git a/src/photos-thumbnailer.h b/src/photos-thumbnailer.h
new file mode 100644
index 0000000..f4d287a
--- /dev/null
+++ b/src/photos-thumbnailer.h
@@ -0,0 +1,35 @@
+/*
+ * Photos - access, organize and share your photos on GNOME
+ * Copyright © 2017 Red Hat, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+#ifndef PHOTOS_THUMBNAILER_H
+#define PHOTOS_THUMBNAILER_H
+
+#include <gio/gio.h>
+
+G_BEGIN_DECLS
+
+#define PHOTOS_TYPE_THUMBNAILER (photos_thumbnailer_get_type ())
+G_DECLARE_FINAL_TYPE (PhotosThumbnailer, photos_thumbnailer, PHOTOS, THUMBNAILER, GApplication);
+
+GApplication          *photos_thumbnailer_new                    (void);
+
+G_END_DECLS
+
+#endif /* PHOTOS_THUMBNAILER_H */


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]