From a16d06a40a02dcb85ad7827587038193b9043dca Mon Sep 17 00:00:00 2001 From: Ell Date: Wed, 9 Jan 2019 13:33:06 -0500 Subject: [PATCH] Issue #1824 - Crash on 2.10.4 using tablet In GimpTool, track the last-seen pointer coordinates, modifier state, and event time, during button_press() and motion() events and use those to synthesize a button_release() event when comitting/halting the tool, if the tool is still active, and a matching button_release() event has not been received. The paint tools (as well as other tools) require each button_press() event to be matched by a button_release() event in order to properly finish their operation, but one isn't organically generated when switching tools due to a device change. (cherry picked from commit 9b25611857c75e922dd10f3d933807b0c7cd44ed) --- app/tools/gimptool.c | 34 ++++++++++++++++++++++++++++++++++ app/tools/gimptool.h | 6 ++++++ 2 files changed, 40 insertions(+) diff --git a/app/tools/gimptool.c b/app/tools/gimptool.c index 52370cdfd3..53981c1aaa 100644 --- a/app/tools/gimptool.c +++ b/app/tools/gimptool.c @@ -138,6 +138,7 @@ static void gimp_tool_options_notify (GimpToolOptions *options, const GParamSpec *pspec, GimpTool *tool); static void gimp_tool_clear_status (GimpTool *tool); +static void gimp_tool_release (GimpTool *tool); G_DEFINE_TYPE_WITH_CODE (GimpTool, gimp_tool, GIMP_TYPE_OBJECT, @@ -649,6 +650,8 @@ gimp_tool_control (GimpTool *tool, { g_return_if_fail (GIMP_IS_TOOL (tool)); + g_object_ref (tool); + switch (action) { case GIMP_TOOL_ACTION_PAUSE: @@ -674,6 +677,8 @@ gimp_tool_control (GimpTool *tool, break; case GIMP_TOOL_ACTION_COMMIT: + gimp_tool_release (tool); + GIMP_TOOL_GET_CLASS (tool)->control (tool, action, display); /* always HALT after COMMIT here and not in each tool individually; @@ -685,6 +690,8 @@ gimp_tool_control (GimpTool *tool, /* pass through */ case GIMP_TOOL_ACTION_HALT: + gimp_tool_release (tool); + GIMP_TOOL_GET_CLASS (tool)->control (tool, action, display); if (gimp_tool_control_is_active (tool->control)) @@ -693,6 +700,8 @@ gimp_tool_control (GimpTool *tool, gimp_tool_clear_status (tool); break; } + + g_object_unref (tool); } void @@ -716,6 +725,10 @@ gimp_tool_button_press (GimpTool *tool, tool->button_press_state = state; tool->active_modifier_state = state; + tool->last_pointer_coords = *coords; + tool->last_pointer_time = time - g_get_monotonic_time () / 1000; + tool->last_pointer_state = state; + if (gimp_tool_control_get_wants_click (tool->control)) { tool->in_click_distance = TRUE; @@ -789,6 +802,8 @@ gimp_tool_button_release (GimpTool *tool, g_object_ref (tool); + tool->last_pointer_state = 0; + my_coords = *coords; if (state & GDK_BUTTON3_MASK) @@ -855,6 +870,10 @@ gimp_tool_motion (GimpTool *tool, tool->got_motion_event = TRUE; + tool->last_pointer_coords = *coords; + tool->last_pointer_time = time - g_get_monotonic_time () / 1000; + tool->last_pointer_state = state; + GIMP_TOOL_GET_CLASS (tool)->motion (tool, coords, time, state, display); } @@ -1476,3 +1495,18 @@ gimp_tool_clear_status (GimpTool *tool) while (tool->status_displays) gimp_tool_pop_status (tool, tool->status_displays->data); } + +static void +gimp_tool_release (GimpTool *tool) +{ + if (tool->last_pointer_state && + gimp_tool_control_is_active (tool->control)) + { + gimp_tool_button_release ( + tool, + &tool->last_pointer_coords, + tool->last_pointer_time + g_get_monotonic_time () / 1000, + tool->last_pointer_state, + tool->display); + } +} diff --git a/app/tools/gimptool.h b/app/tools/gimptool.h index f013637745..c7943fd905 100644 --- a/app/tools/gimptool.h +++ b/app/tools/gimptool.h @@ -60,6 +60,12 @@ struct _GimpTool GdkModifierType button_press_state; GdkModifierType active_modifier_state; + /* private state for synthesizing button_release() events + */ + GimpCoords last_pointer_coords; + guint32 last_pointer_time; + GdkModifierType last_pointer_state; + /* private state for click detection */ gboolean in_click_distance;