From ddfc7715cbcd8d165520902bc0abfff3c09b88f4 Mon Sep 17 00:00:00 2001 From: Ell Date: Mon, 16 Apr 2018 09:49:15 -0400 Subject: [PATCH] app: make the airbrush tool thread-safe w.r.t. paint thread GimpAirbrush currently performs painting and flushes the image on its own during the airbrush timeout. This is unsafe w.r.t. the paint thread, since the timeout is run on the main thread, while paint commands should run on the paint thread. Add a "timeout" signal to GimpAirbrush, and simply emit this signal during the airbrush timeout, rather than actually painting. Connect to this signal in GimpAirbrushTool, and use gimppaintool-paint to perform the actual painting, in a thread-safe manner (see the previous commit.) --- app/paint/gimpairbrush.c | 37 +++++++++++++++++++----------------- app/paint/gimpairbrush.h | 11 +++++------ app/tools/gimpairbrushtool.c | 33 +++++++++++++++++++++++++++++++- 3 files changed, 57 insertions(+), 24 deletions(-) diff --git a/app/paint/gimpairbrush.c b/app/paint/gimpairbrush.c index 8ba5e9737e..042cb2b33b 100644 --- a/app/paint/gimpairbrush.c +++ b/app/paint/gimpairbrush.c @@ -36,6 +36,13 @@ #include "gimp-intl.h" +enum +{ + TIMEOUT, + LAST_SIGNAL +}; + + static void gimp_airbrush_finalize (GObject *object); static void gimp_airbrush_paint (GimpPaintCore *paint_core, @@ -56,6 +63,8 @@ G_DEFINE_TYPE (GimpAirbrush, gimp_airbrush, GIMP_TYPE_PAINTBRUSH) #define parent_class gimp_airbrush_parent_class +static guint airbrush_signals[LAST_SIGNAL] = { 0 }; + void gimp_airbrush_register (Gimp *gimp, @@ -78,6 +87,15 @@ gimp_airbrush_class_init (GimpAirbrushClass *klass) object_class->finalize = gimp_airbrush_finalize; paint_core_class->paint = gimp_airbrush_paint; + + airbrush_signals[TIMEOUT] = + g_signal_new ("timeout", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (GimpAirbrushClass, timeout), + NULL, NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0); } static void @@ -96,8 +114,6 @@ gimp_airbrush_finalize (GObject *object) airbrush->timeout_id = 0; } - g_clear_object (&airbrush->sym); - G_OBJECT_CLASS (parent_class)->finalize (object); } @@ -142,13 +158,6 @@ gimp_airbrush_paint (GimpPaintCore *paint_core, fade_point = gimp_paint_options_get_fade (paint_options, image, paint_core->pixel_dist); - airbrush->drawable = drawable; - airbrush->paint_options = paint_options; - - if (airbrush->sym) - g_object_unref (airbrush->sym); - airbrush->sym = g_object_ref (sym); - /* Base our timeout on the original stroke. */ coords = gimp_symmetry_get_origin (sym); @@ -171,8 +180,6 @@ gimp_airbrush_paint (GimpPaintCore *paint_core, paint_options, sym, paint_state, time); - - g_clear_object (&airbrush->sym); break; } } @@ -212,13 +219,9 @@ gimp_airbrush_timeout (gpointer data) { GimpAirbrush *airbrush = GIMP_AIRBRUSH (data); - gimp_airbrush_paint (GIMP_PAINT_CORE (airbrush), - airbrush->drawable, - airbrush->paint_options, - airbrush->sym, - GIMP_PAINT_STATE_MOTION, 0); + airbrush->timeout_id = 0; - gimp_image_flush (gimp_item_get_image (GIMP_ITEM (airbrush->drawable))); + g_signal_emit (airbrush, airbrush_signals[TIMEOUT], 0); return G_SOURCE_REMOVE; } diff --git a/app/paint/gimpairbrush.h b/app/paint/gimpairbrush.h index a3f9a8a03c..7aa802cc53 100644 --- a/app/paint/gimpairbrush.h +++ b/app/paint/gimpairbrush.h @@ -34,18 +34,17 @@ typedef struct _GimpAirbrushClass GimpAirbrushClass; struct _GimpAirbrush { - GimpPaintbrush parent_instance; + GimpPaintbrush parent_instance; - guint timeout_id; - - GimpSymmetry *sym; - GimpDrawable *drawable; - GimpPaintOptions *paint_options; + guint timeout_id; }; struct _GimpAirbrushClass { GimpPaintbrushClass parent_class; + + /* signals */ + void (* timeout) (GimpAirbrush *airbrush); }; diff --git a/app/tools/gimpairbrushtool.c b/app/tools/gimpairbrushtool.c index 62796a77f9..2374a372c5 100644 --- a/app/tools/gimpairbrushtool.c +++ b/app/tools/gimpairbrushtool.c @@ -31,16 +31,24 @@ #include "gimpairbrushtool.h" #include "gimppaintoptions-gui.h" +#include "gimppainttool-paint.h" #include "gimptoolcontrol.h" #include "gimp-intl.h" -static GtkWidget * gimp_airbrush_options_gui (GimpToolOptions *tool_options); +static void gimp_airbrush_tool_constructed (GObject *object); + +static void gimp_airbrush_tool_airbrush_timeout (GimpAirbrush *airbrush, + GimpAirbrushTool *airbrush_tool); + +static GtkWidget * gimp_airbrush_options_gui (GimpToolOptions *tool_options); G_DEFINE_TYPE (GimpAirbrushTool, gimp_airbrush_tool, GIMP_TYPE_PAINTBRUSH_TOOL) +#define parent_class gimp_airbrush_tool_parent_class + void gimp_airbrush_tool_register (GimpToolRegisterCallback callback, @@ -63,6 +71,9 @@ gimp_airbrush_tool_register (GimpToolRegisterCallback callback, static void gimp_airbrush_tool_class_init (GimpAirbrushToolClass *klass) { + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->constructed = gimp_airbrush_tool_constructed; } static void @@ -73,6 +84,26 @@ gimp_airbrush_tool_init (GimpAirbrushTool *airbrush) gimp_tool_control_set_tool_cursor (tool->control, GIMP_TOOL_CURSOR_AIRBRUSH); } +static void +gimp_airbrush_tool_constructed (GObject *object) +{ + GimpPaintTool *paint_tool = GIMP_PAINT_TOOL (object); + + G_OBJECT_CLASS (parent_class)->constructed (object); + + g_signal_connect_object (paint_tool->core, "timeout", + G_CALLBACK (gimp_airbrush_tool_airbrush_timeout), + object, 0); +} + +static void +gimp_airbrush_tool_airbrush_timeout (GimpAirbrush *airbrush, + GimpAirbrushTool *airbrush_tool) +{ + gimp_paint_tool_paint_core_paint (GIMP_PAINT_TOOL (airbrush_tool), + GIMP_PAINT_STATE_MOTION, 0); +} + /* tool options stuff */