[gimp/wip/Jehan/classy-GIMP: 15/17] app, libgimp: add some first concept of signalling to plug-ins.



commit 10de031f1df8fb70264821c5e900992ef1a50c11
Author: Jehan <jehan girinstud io>
Date:   Thu Aug 15 21:55:36 2019 +0200

    app, libgimp: add some first concept of signalling to plug-ins.
    
    This is still an early PoC, and so far I have only implemented a
    "destroyed" signal on libgimp's GimpImage. This way, plug-ins can react
    on destroyed images instead of crashing.
    When going further, we will want to allow more complex signals with
    parameters and returned values, just like for procedure. This is similar
    to have standard procedures which any plug-in can hook on through
    GObject signals.

 app/core/gimpimage.c             |  6 +++
 app/plug-in/gimpplugin-message.c |  9 +++++
 app/plug-in/gimpplugin.c         | 32 +++++++++++++++
 app/plug-in/gimpplugin.h         |  4 ++
 app/plug-in/gimppluginmanager.c  | 12 ++++++
 app/plug-in/gimppluginmanager.h  |  5 +++
 libgimp/gimpdisplay.c            | 23 +++++++++++
 libgimp/gimpdisplay.h            |  5 +++
 libgimp/gimpimage.c              | 61 +++++++++++++++++++++++++++-
 libgimp/gimpimage.h              |  9 +++++
 libgimp/gimpitem.c               | 26 ++++++++++++
 libgimp/gimpitem.h               |  6 +++
 libgimp/gimplegacy.c             |  7 ++++
 libgimp/gimpplugin-private.c     | 37 +++++++++++++++++
 libgimpbase/gimpprotocol.c       | 87 ++++++++++++++++++++++++++++++++++++++++
 libgimpbase/gimpprotocol.h       | 22 +++++++++-
 16 files changed, 349 insertions(+), 2 deletions(-)
---
diff --git a/app/core/gimpimage.c b/app/core/gimpimage.c
index 379d327ce3..e06c44dbd8 100644
--- a/app/core/gimpimage.c
+++ b/app/core/gimpimage.c
@@ -36,6 +36,8 @@
 
 #include "operations/layer-modes/gimp-layer-modes.h"
 
+#include "plug-in/gimppluginmanager.h"
+
 #include "gegl/gimp-babl.h"
 
 #include "gimp.h"
@@ -1039,6 +1041,10 @@ gimp_image_finalize (GObject *object)
   g_clear_object (&private->graph);
   private->visible_mask = NULL;
 
+  gimp_plug_in_manager_emit_signal (image->gimp->plug_in_manager,
+                                    object, gimp_image_get_ID (image),
+                                    "destroyed");
+
   if (private->colormap)
     gimp_image_colormap_free (image);
 
diff --git a/app/plug-in/gimpplugin-message.c b/app/plug-in/gimpplugin-message.c
index 6aecb72d84..8144994003 100644
--- a/app/plug-in/gimpplugin-message.c
+++ b/app/plug-in/gimpplugin-message.c
@@ -160,6 +160,15 @@ gimp_plug_in_handle_message (GimpPlugIn      *plug_in,
     case GP_HAS_INIT:
       gimp_plug_in_handle_has_init (plug_in);
       break;
+
+    case GP_SIGNAL:
+      gimp_message (plug_in->manager->gimp, NULL, GIMP_MESSAGE_ERROR,
+                    "Plug-in \"%s\"\n(%s)\n\n"
+                    "sent a SIGNAL message.  This should not happen.",
+                    gimp_object_get_name (plug_in),
+                    gimp_file_get_utf8_name (plug_in->file));
+      gimp_plug_in_close (plug_in, TRUE);
+      break;
     }
 }
 
diff --git a/app/plug-in/gimpplugin.c b/app/plug-in/gimpplugin.c
index 908e6a69d5..ee51dd3fb3 100644
--- a/app/plug-in/gimpplugin.c
+++ b/app/plug-in/gimpplugin.c
@@ -75,6 +75,8 @@
 #include "plug-in-types.h"
 
 #include "core/gimp.h"
+#include "core/gimpimage.h"
+#include "core/gimpitem.h"
 #include "core/gimp-spawn.h"
 #include "core/gimpprogress.h"
 
@@ -1044,3 +1046,33 @@ gimp_plug_in_get_error_handler (GimpPlugIn *plug_in)
 
   return GIMP_PDB_ERROR_HANDLER_INTERNAL;
 }
+
+void
+gimp_plug_in_emit_signal (GimpPlugIn   *plug_in,
+                          GObject      *object,
+                          gint32        id,
+                          const gchar  *name)
+{
+  if (plug_in->open)
+    {
+      GPSignalType type = GP_SIGNAL_TYPE_NONE;
+
+      if (GIMP_IS_IMAGE (object))
+        type = GP_SIGNAL_TYPE_IMAGE;
+      else if (GIMP_IS_ITEM (object))
+        type = GP_SIGNAL_TYPE_ITEM;
+      else if (g_strcmp0 (G_OBJECT_TYPE_NAME (object), "GimpDisplay") == 0)
+        type = GP_SIGNAL_TYPE_DISPLAY;
+
+      if (type != GP_SIGNAL_TYPE_NONE)
+        {
+          GPSignal signal;
+
+          signal.type = type;
+          signal.id   = id;
+          signal.name = (gchar *) name;
+
+          gp_signal_write (plug_in->my_write, &signal, plug_in);
+        }
+    }
+}
diff --git a/app/plug-in/gimpplugin.h b/app/plug-in/gimpplugin.h
index 2cd3756467..82a6a51b11 100644
--- a/app/plug-in/gimpplugin.h
+++ b/app/plug-in/gimpplugin.h
@@ -119,5 +119,9 @@ void          gimp_plug_in_set_error_handler (GimpPlugIn             *plug_in,
 GimpPDBErrorHandler
               gimp_plug_in_get_error_handler (GimpPlugIn             *plug_in);
 
+void          gimp_plug_in_emit_signal       (GimpPlugIn             *plug_in,
+                                              GObject                *object,
+                                              gint32                  id,
+                                              const gchar            *name);
 
 #endif /* __GIMP_PLUG_IN_H__ */
diff --git a/app/plug-in/gimppluginmanager.c b/app/plug-in/gimppluginmanager.c
index d8952a289f..e9479d4ffa 100644
--- a/app/plug-in/gimppluginmanager.c
+++ b/app/plug-in/gimppluginmanager.c
@@ -424,3 +424,15 @@ gimp_plug_in_manager_plug_in_pop (GimpPlugInManager *manager)
   else
     manager->current_plug_in = NULL;
 }
+
+void
+gimp_plug_in_manager_emit_signal (GimpPlugInManager *manager,
+                                  GObject           *object,
+                                  gint32             id,
+                                  const gchar       *name)
+{
+  GSList *iter = manager->open_plug_ins;
+
+  for (; iter; iter = iter->next)
+    gimp_plug_in_emit_signal (iter->data, object, id, name);
+}
diff --git a/app/plug-in/gimppluginmanager.h b/app/plug-in/gimppluginmanager.h
index 11f80b120f..1192083d7b 100644
--- a/app/plug-in/gimppluginmanager.h
+++ b/app/plug-in/gimppluginmanager.h
@@ -114,5 +114,10 @@ void    gimp_plug_in_manager_plug_in_push         (GimpPlugInManager   *manager,
                                                    GimpPlugIn          *plug_in);
 void    gimp_plug_in_manager_plug_in_pop          (GimpPlugInManager   *manager);
 
+void    gimp_plug_in_manager_emit_signal          (GimpPlugInManager   *manager,
+                                                   GObject             *object,
+                                                   gint32               id,
+                                                   const gchar         *name);
+
 
 #endif  /* __GIMP_PLUG_IN_MANAGER_H__ */
diff --git a/libgimp/gimpdisplay.c b/libgimp/gimpdisplay.c
index 419fd59074..7c1fbbec0a 100644
--- a/libgimp/gimpdisplay.c
+++ b/libgimp/gimpdisplay.c
@@ -183,3 +183,26 @@ gimp_display_get_by_id (gint32 display_id)
 
   return display;
 }
+
+
+/* Internal API. */
+
+
+G_GNUC_INTERNAL
+void
+_gimp_display_process_signal (gint32       display_id,
+                              const gchar *name)
+{
+  GimpDisplay *display = NULL;
+
+  if (! gimp_displays)
+    return;
+
+  display = g_hash_table_lookup (gimp_displays,
+                                 GINT_TO_POINTER (display_id));
+
+  if (! display)
+    return;
+
+  /* Below process display signals. */
+}
diff --git a/libgimp/gimpdisplay.h b/libgimp/gimpdisplay.h
index 7abfb0b053..1709e4407c 100644
--- a/libgimp/gimpdisplay.h
+++ b/libgimp/gimpdisplay.h
@@ -70,6 +70,11 @@ gint32        gimp_display_get_id       (GimpDisplay    *display);
 GimpDisplay * gimp_display_get_by_id    (gint32          display_id);
 
 
+G_GNUC_INTERNAL
+void          _gimp_display_process_signal (gint32        display_id,
+                                            const gchar  *name);
+
+
 G_END_DECLS
 
 #endif /* __GIMP_DISPLAY_H__ */
diff --git a/libgimp/gimpimage.c b/libgimp/gimpimage.c
index b3cc11686a..7355ac7ceb 100644
--- a/libgimp/gimpimage.c
+++ b/libgimp/gimpimage.c
@@ -24,6 +24,12 @@
 
 #include "gimppixbuf.h"
 
+enum
+{
+  DESTROYED,
+  LAST_SIGNAL
+};
+
 enum
 {
   PROP_0,
@@ -52,7 +58,8 @@ G_DEFINE_TYPE_WITH_PRIVATE (GimpImage, gimp_image, G_TYPE_OBJECT)
 
 #define parent_class gimp_image_parent_class
 
-static GParamSpec *props[N_PROPS] = { NULL, };
+static GParamSpec *props[N_PROPS]       = { NULL, };
+static guint       signals[LAST_SIGNAL] = { 0 };
 
 static void
 gimp_image_class_init (GimpImageClass *klass)
@@ -62,6 +69,26 @@ gimp_image_class_init (GimpImageClass *klass)
   object_class->set_property = gimp_image_set_property;
   object_class->get_property = gimp_image_get_property;
 
+  /**
+   * GimpImageClass::destroy:
+   * @image: a #GimpImage
+   *
+   * This signal will be emitted when an image has been destroyed, just
+   * before we g_object_unref() it. This image is now invalid, none of
+   * its data can be accessed anymore, therefore all processing has to
+   * be stopped immediately.
+   * The only thing still feasible is to compare the image if you kept a
+   * reference to identify images, or to run gimp_image_get_id().
+   */
+  signals[DESTROYED] =
+    g_signal_new ("destroyed",
+                  G_TYPE_FROM_CLASS (klass),
+                  G_SIGNAL_RUN_FIRST,
+                  G_STRUCT_OFFSET (GimpImageClass, destroyed),
+                  NULL, NULL,
+                  g_cclosure_marshal_VOID__VOID,
+                  G_TYPE_NONE, 0);
+
   props[PROP_ID] =
     g_param_spec_int ("id",
                       "The image id",
@@ -511,6 +538,38 @@ gimp_image_set_metadata (GimpImage    *image,
 }
 
 
+/* Internal API. */
+
+
+void
+_gimp_image_process_signal (gint32       image_id,
+                            const gchar *name)
+{
+  GimpImage *image = NULL;
+
+  if (! gimp_images)
+    return;
+
+  image = g_hash_table_lookup (gimp_images,
+                               GINT_TO_POINTER (image_id));
+
+  if (! image)
+    /* No need to create images not referenced by the plug-in. */
+    return;
+
+  /* Below process image signals. */
+
+  if (g_strcmp0 (name, "destroyed") == 0)
+    {
+      g_hash_table_steal (gimp_images,
+                          GINT_TO_POINTER (image->priv->id));
+
+      g_signal_emit (image, signals[DESTROYED], 0);
+      g_object_unref (image);
+    }
+}
+
+
 /* Deprecated API. */
 
 
diff --git a/libgimp/gimpimage.h b/libgimp/gimpimage.h
index d63c5a4f74..cdec1f5882 100644
--- a/libgimp/gimpimage.h
+++ b/libgimp/gimpimage.h
@@ -51,6 +51,9 @@ struct _GimpImageClass
 {
   GObjectClass parent_class;
 
+  /* Signals. */
+  void (* destroyed) (GimpImage *image);
+
   /* Padding for future expansion */
   void (*_gimp_reserved1) (void);
   void (*_gimp_reserved2) (void);
@@ -70,6 +73,12 @@ GimpImage    * gimp_image_get_by_id          (gint32        image_id);
 
 GList        * gimp_image_list               (void);
 
+
+G_GNUC_INTERNAL
+void           _gimp_image_process_signal    (gint32        image_id,
+                                              const gchar  *name);
+
+
 #ifndef GIMP_DEPRECATED_REPLACE_NEW_API
 
 GList        * gimp_image_get_layers         (GimpImage    *image);
diff --git a/libgimp/gimpitem.c b/libgimp/gimpitem.c
index addde8a1bc..066fb2a368 100644
--- a/libgimp/gimpitem.c
+++ b/libgimp/gimpitem.c
@@ -243,6 +243,32 @@ gimp_item_get_children (GimpItem *item)
   return children;
 }
 
+
+/* Internal API. */
+
+
+void
+_gimp_item_process_signal (gint32       item_id,
+                           const gchar *name)
+{
+  GimpItem *item = NULL;
+
+  if (! gimp_items)
+    return;
+
+  item = g_hash_table_lookup (gimp_items,
+                              GINT_TO_POINTER (item_id));
+
+  if (! item)
+    return;
+
+  /* Below process item signals. */
+}
+
+
+/* Deprecated API. */
+
+
 /**
  * gimp_item_get_children_deprecated: (skip)
  * @item_id: The item.
diff --git a/libgimp/gimpitem.h b/libgimp/gimpitem.h
index 0013e3e6eb..3d948e955f 100644
--- a/libgimp/gimpitem.h
+++ b/libgimp/gimpitem.h
@@ -69,6 +69,12 @@ GType         gimp_item_get_type     (void) G_GNUC_CONST;
 gint32        gimp_item_get_id       (GimpItem    *item);
 GimpItem    * gimp_item_get_by_id    (gint32       item_id);
 
+
+G_GNUC_INTERNAL
+void          _gimp_item_process_signal    (gint32        item_id,
+                                            const gchar  *name);
+
+
 #ifndef GIMP_DEPRECATED_REPLACE_NEW_API
 
 GList       * gimp_item_get_children (GimpItem     *item);
diff --git a/libgimp/gimplegacy.c b/libgimp/gimplegacy.c
index ffb48095c9..ed3ee94641 100644
--- a/libgimp/gimplegacy.c
+++ b/libgimp/gimplegacy.c
@@ -677,6 +677,10 @@ gimp_loop (GimpRunProc run_proc)
         case GP_HAS_INIT:
           g_warning ("unexpected has init message received (should not happen)");
           break;
+
+        case GP_SIGNAL:
+          g_warning ("unexpected signal message received (should not happen)");
+          break;
         }
 
       gimp_wire_destroy (&msg);
@@ -717,6 +721,9 @@ gimp_process_message (GimpWireMessage *msg)
     case GP_HAS_INIT:
       g_warning ("unexpected has init message received (should not happen)");
       break;
+    case GP_SIGNAL:
+      g_warning ("signal message received; not supported with legacy API");
+      break;
     }
 }
 
diff --git a/libgimp/gimpplugin-private.c b/libgimp/gimpplugin-private.c
index 3366831bf5..31c50adeec 100644
--- a/libgimp/gimpplugin-private.c
+++ b/libgimp/gimpplugin-private.c
@@ -42,6 +42,9 @@ static void       gimp_plug_in_register          (GimpPlugIn      *plug_in,
 static void       gimp_plug_in_loop              (GimpPlugIn      *plug_in);
 static void       gimp_plug_in_process_message   (GimpPlugIn      *plug_in,
                                                   GimpWireMessage *msg);
+
+static void       gimp_plug_in_signal            (GimpPlugIn      *plug_in,
+                                                  GPSignal        *signal);
 static void       gimp_plug_in_proc_run          (GimpPlugIn      *plug_in,
                                                   GPProcRun       *proc_run);
 static void       gimp_plug_in_temp_proc_run     (GimpPlugIn      *plug_in,
@@ -148,6 +151,11 @@ _gimp_plug_in_read_expect_msg (GimpPlugIn      *plug_in,
         {
           gimp_plug_in_process_message (plug_in, msg);
         }
+      else if (msg->type == GP_SIGNAL)
+        {
+          gimp_plug_in_signal (plug_in, msg->data);
+          continue;
+        }
       else
         {
           g_error ("unexpected message: %d", msg->type);
@@ -279,6 +287,10 @@ gimp_plug_in_loop (GimpPlugIn *plug_in)
         case GP_HAS_INIT:
           g_warning ("unexpected has init message received (should not happen)");
           break;
+
+        case GP_SIGNAL:
+          g_warning ("unexpected signal message received (should not happen)");
+          break;
         }
 
       gimp_wire_destroy (&msg);
@@ -334,6 +346,31 @@ gimp_plug_in_process_message (GimpPlugIn      *plug_in,
     case GP_HAS_INIT:
       g_warning ("unexpected has init message received (should not happen)");
       break;
+    case GP_SIGNAL:
+      gimp_plug_in_signal (plug_in, msg->data);
+      break;
+    }
+}
+
+static void
+gimp_plug_in_signal (GimpPlugIn *plug_in,
+                     GPSignal   *signal)
+{
+  switch (signal->type)
+    {
+    case GP_SIGNAL_TYPE_IMAGE:
+      _gimp_image_process_signal (signal->id, signal->name);
+      break;
+    case GP_SIGNAL_TYPE_ITEM:
+      _gimp_item_process_signal (signal->id, signal->name);
+      break;
+    case GP_SIGNAL_TYPE_DISPLAY:
+      _gimp_display_process_signal (signal->id, signal->name);
+      break;
+
+    case GP_SIGNAL_TYPE_NONE:
+      g_warning ("Unexpected signal without type received (should not happen)");
+      break;
     }
 }
 
diff --git a/libgimpbase/gimpprotocol.c b/libgimpbase/gimpprotocol.c
index 3add813851..ae0affd3d5 100644
--- a/libgimpbase/gimpprotocol.c
+++ b/libgimpbase/gimpprotocol.c
@@ -144,6 +144,13 @@ static void _gp_has_init_write           (GIOChannel       *channel,
                                           gpointer          user_data);
 static void _gp_has_init_destroy         (GimpWireMessage  *msg);
 
+static void _gp_signal_read              (GIOChannel       *channel,
+                                          GimpWireMessage  *msg,
+                                          gpointer          user_data);
+static void _gp_signal_write             (GIOChannel       *channel,
+                                          GimpWireMessage  *msg,
+                                          gpointer          user_data);
+static void _gp_signal_destroy           (GimpWireMessage  *msg);
 
 
 void
@@ -201,6 +208,10 @@ gp_init (void)
                       _gp_has_init_read,
                       _gp_has_init_write,
                       _gp_has_init_destroy);
+  gimp_wire_register (GP_SIGNAL,
+                      _gp_signal_read,
+                      _gp_signal_write,
+                      _gp_signal_destroy);
 }
 
 /* public writing API */
@@ -448,6 +459,25 @@ gp_has_init_write (GIOChannel *channel,
   return TRUE;
 }
 
+gboolean
+gp_signal_write (GIOChannel *channel,
+                 GPSignal   *signal,
+                 gpointer    user_data)
+{
+  GimpWireMessage msg;
+
+  msg.type = GP_SIGNAL;
+  msg.data = signal;
+
+  if (! gimp_wire_write_msg (channel, &msg, user_data))
+    return FALSE;
+
+  if (! gimp_wire_flush (channel, user_data))
+    return FALSE;
+
+  return TRUE;
+}
+
 /*  quit  */
 
 static void
@@ -1953,3 +1983,60 @@ static void
 _gp_has_init_destroy (GimpWireMessage *msg)
 {
 }
+
+static void
+_gp_signal_read (GIOChannel      *channel,
+                 GimpWireMessage *msg,
+                 gpointer         user_data)
+{
+  GPSignal *signal = g_slice_new0 (GPSignal);
+
+  if (! _gimp_wire_read_int32 (channel,
+                               &signal->type, 1, user_data))
+    goto cleanup;
+  if (! _gimp_wire_read_int32 (channel,
+                               &signal->id, 1, user_data))
+    goto cleanup;
+  if (! _gimp_wire_read_string (channel,
+                                &signal->name, 1, user_data))
+    goto cleanup;
+
+  msg->data = signal;
+  return;
+
+ cleanup:
+  g_clear_pointer (&signal->name, g_free);
+
+  g_slice_free (GPSignal, signal);
+  msg->data = NULL;
+}
+
+static void
+_gp_signal_write (GIOChannel      *channel,
+                  GimpWireMessage *msg,
+                  gpointer         user_data)
+{
+  GPSignal *signal = msg->data;
+
+  if (! _gimp_wire_write_int32 (channel,
+                                &signal->type, 1, user_data))
+    return;
+  if (! _gimp_wire_write_int32 (channel,
+                                &signal->id, 1, user_data))
+    return;
+  if (! _gimp_wire_write_string (channel,
+                                 &signal->name, 1, user_data))
+    return;
+}
+
+static void
+_gp_signal_destroy (GimpWireMessage *msg)
+{
+  GPSignal *signal = msg->data;
+
+  if (signal)
+    {
+      g_free (signal->name);
+      g_slice_free (GPSignal, signal);
+    }
+}
diff --git a/libgimpbase/gimpprotocol.h b/libgimpbase/gimpprotocol.h
index effb494616..13fda1ac6b 100644
--- a/libgimpbase/gimpprotocol.h
+++ b/libgimpbase/gimpprotocol.h
@@ -43,7 +43,8 @@ enum
   GP_PROC_INSTALL,
   GP_PROC_UNINSTALL,
   GP_EXTENSION_ACK,
-  GP_HAS_INIT
+  GP_HAS_INIT,
+  GP_SIGNAL
 };
 
 typedef enum
@@ -72,6 +73,14 @@ typedef enum
   GP_PARAM_TYPE_PARAM_DEF
 } GPParamType;
 
+typedef enum
+{
+  GP_SIGNAL_TYPE_NONE,
+  GP_SIGNAL_TYPE_IMAGE,
+  GP_SIGNAL_TYPE_ITEM,
+  GP_SIGNAL_TYPE_DISPLAY
+} GPSignalType;
+
 
 typedef struct _GPConfig           GPConfig;
 typedef struct _GPTileReq          GPTileReq;
@@ -94,6 +103,7 @@ typedef struct _GPProcRun          GPProcRun;
 typedef struct _GPProcReturn       GPProcReturn;
 typedef struct _GPProcInstall      GPProcInstall;
 typedef struct _GPProcUninstall    GPProcUninstall;
+typedef struct _GPSignal           GPSignal;
 
 
 struct _GPConfig
@@ -286,6 +296,13 @@ struct _GPProcUninstall
   gchar *name;
 };
 
+struct _GPSignal
+{
+  GPSignalType  type;
+  guint32       id;
+  gchar        *name;
+
+};
 
 void      gp_init                   (void);
 
@@ -324,6 +341,9 @@ gboolean  gp_extension_ack_write    (GIOChannel      *channel,
                                      gpointer         user_data);
 gboolean  gp_has_init_write         (GIOChannel      *channel,
                                      gpointer         user_data);
+gboolean  gp_signal_write           (GIOChannel      *channel,
+                                     GPSignal        *signal,
+                                     gpointer         user_data);
 
 
 G_END_DECLS


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