[gimp/gimp-2-10] app: avoid dropping motion events in spin-scales



commit 05341fada37d2a340af957475602ef01192a6060
Author: Ell <ell_se yahoo com>
Date:   Mon Aug 24 12:30:57 2020 +0300

    app: avoid dropping motion events in spin-scales
    
    It looks like GDK motion hints are broken, at least on X, causing
    us to drop motion events in GimpSpinScale, if the motion triggers
    an operation that blocks the main thread for a significant amount
    of time, such as projection invalidation.
    
    Instead, disable motion hints for spin-scales, and use an idle to
    perform ad-hoc motion compression.

 app/widgets/gimpspinscale.c | 96 ++++++++++++++++++++++++++++++++++++++++++---
 1 file changed, 91 insertions(+), 5 deletions(-)
---
diff --git a/app/widgets/gimpspinscale.c b/app/widgets/gimpspinscale.c
index afd742df3e..7fa4039bed 100644
--- a/app/widgets/gimpspinscale.c
+++ b/app/widgets/gimpspinscale.c
@@ -87,6 +87,9 @@ struct _GimpSpinScalePrivate
   gboolean         pointer_warp;
   gint             pointer_warp_x;
   gint             pointer_warp_start_x;
+
+  gint             change_value_idle_id;
+  gdouble          change_value_idle_value;
 };
 
 #define GET_PRIVATE(obj) ((GimpSpinScalePrivate *) gimp_spin_scale_get_instance_private ((GimpSpinScale *) 
(obj)))
@@ -103,6 +106,7 @@ static void       gimp_spin_scale_get_property      (GObject          *object,
                                                      GValue           *value,
                                                      GParamSpec       *pspec);
 
+static void       gimp_spin_scale_map               (GtkWidget        *widget);
 static void       gimp_spin_scale_size_request      (GtkWidget        *widget,
                                                      GtkRequisition   *requisition);
 static void       gimp_spin_scale_style_set         (GtkWidget        *widget,
@@ -155,6 +159,7 @@ gimp_spin_scale_class_init (GimpSpinScaleClass *klass)
   object_class->set_property         = gimp_spin_scale_set_property;
   object_class->get_property         = gimp_spin_scale_get_property;
 
+  widget_class->map                  = gimp_spin_scale_map;
   widget_class->size_request         = gimp_spin_scale_size_request;
   widget_class->style_set            = gimp_spin_scale_style_set;
   widget_class->expose_event         = gimp_spin_scale_expose;
@@ -212,6 +217,19 @@ gimp_spin_scale_dispose (GObject *object)
 
   g_clear_object (&private->layout);
 
+  if (private->change_value_idle_id)
+    {
+      GtkAdjustment *adjustment;
+
+      adjustment = gtk_spin_button_get_adjustment (GTK_SPIN_BUTTON (object));
+
+      g_source_remove (private->change_value_idle_id);
+
+      private->change_value_idle_id = 0;
+
+      gtk_adjustment_set_value (adjustment, private->change_value_idle_value);
+    }
+
   G_OBJECT_CLASS (parent_class)->dispose (object);
 }
 
@@ -267,6 +285,36 @@ gimp_spin_scale_get_property (GObject    *object,
     }
 }
 
+static void
+gimp_spin_scale_map (GtkWidget *widget)
+{
+  GdkWindow *window;
+
+  GTK_WIDGET_CLASS (parent_class)->map (widget);
+
+  window = gtk_entry_get_text_window (GTK_ENTRY (widget));
+
+  if (window)
+    {
+      /* as per 2020, motion hints seem to be broken, at least on X: calling
+       * gdk_event_request_motions() doesn't seem to generate further motion
+       * events, causing motion events to be discarded, especially if the spin-
+       * scale is tied to some costly operation, such as projection
+       * invalidation, which blocks the main thread.
+       *
+       * to fix this, we simply avoid motion hints for the widget, and use an
+       * idle for setting the spin-scale value in response to motion events, as
+       * a form of ad-hoc motion compression.
+       *
+       * note that this isn't necessary with gtk3, which does its own motion
+       * compression.
+       */
+      gdk_window_set_events (window,
+                             gdk_window_get_events (window) &
+                             ~GDK_POINTER_MOTION_HINT_MASK);
+    }
+}
+
 static void
 gimp_spin_scale_size_request (GtkWidget      *widget,
                               GtkRequisition *requisition)
@@ -715,10 +763,26 @@ gimp_spin_scale_get_limits (GimpSpinScale *scale,
     }
 }
 
+static gboolean
+gimp_spin_scale_change_value_idle (GimpSpinScale *scale)
+{
+  GimpSpinScalePrivate *private = GET_PRIVATE (scale);
+  GtkAdjustment        *adjustment;
+
+  private->change_value_idle_id = 0;
+
+  adjustment = gtk_spin_button_get_adjustment (GTK_SPIN_BUTTON (scale));
+
+  gtk_adjustment_set_value (adjustment, private->change_value_idle_value);
+
+  return G_SOURCE_REMOVE;
+}
+
 static void
 gimp_spin_scale_change_value (GtkWidget *widget,
                               gdouble    x,
-                              guint      state)
+                              guint      state,
+                              gboolean   now)
 {
   GimpSpinScalePrivate *private     = GET_PRIVATE (widget);
   GtkSpinButton        *spin_button = GTK_SPIN_BUTTON (widget);
@@ -794,7 +858,26 @@ gimp_spin_scale_change_value (GtkWidget *widget,
   if (private->constrain_drag)
     value = rint (value);
 
-  gtk_adjustment_set_value (adjustment, value);
+  if (now)
+    {
+      if (private->change_value_idle_id)
+        {
+          g_source_remove (private->change_value_idle_id);
+
+          private->change_value_idle_id = 0;
+        }
+
+      gtk_adjustment_set_value (adjustment, value);
+    }
+  else if (! private->change_value_idle_id)
+    {
+      private->change_value_idle_value = value;
+
+      private->change_value_idle_id = g_idle_add_full (
+        G_PRIORITY_DEFAULT + 1,
+        (GSourceFunc) gimp_spin_scale_change_value_idle,
+        widget, NULL);
+    }
 }
 
 static gboolean
@@ -821,7 +904,7 @@ gimp_spin_scale_button_press (GtkWidget      *widget,
 
           gtk_widget_grab_focus (widget);
 
-          gimp_spin_scale_change_value (widget, event->x, event->state);
+          gimp_spin_scale_change_value (widget, event->x, event->state, TRUE);
 
           return TRUE;
 
@@ -863,7 +946,7 @@ gimp_spin_scale_button_release (GtkWidget      *widget,
        * gimp_spin_scale_motion_notify().
        */
       if (! private->pointer_warp)
-        gimp_spin_scale_change_value (widget, event->x, event->state);
+        gimp_spin_scale_change_value (widget, event->x, event->state, TRUE);
 
       if (private->relative_change)
         {
@@ -953,7 +1036,10 @@ gimp_spin_scale_motion_notify (GtkWidget      *widget,
             private->start_x = private->pointer_warp_start_x;
         }
 
-      gimp_spin_scale_change_value (widget, event->x, event->state);
+      /* change the value in an idle, as a form of motion compression, since we
+       * don't use motion hints.  see the comment in gimp_spin_scale_map().
+       */
+      gimp_spin_scale_change_value (widget, event->x, event->state, FALSE);
 
       if (private->relative_change)
         {


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