Bug 791689 - On-Canvas GUI broken with extended input device

Something about the unqueueing and requeueing of the entire event
queue during motion compression fries GTK's brain w.r.t. extended
input events.  Instead, have gimp_display_shell_compress_motion()
return the first non-compressed event to the caller, making it
responsible for dispatching it after handling the motion event.
This commit is contained in:
Ell 2018-01-04 06:02:53 -05:00
parent 0f29e67078
commit fa18044392
1 changed files with 244 additions and 222 deletions

View File

@ -71,62 +71,68 @@
/* local function prototypes */
static gboolean gimp_display_shell_canvas_tool_events_internal (GtkWidget *canvas,
GdkEvent *event,
GimpDisplayShell *shell,
GdkEvent **next_event);
static GdkModifierType
gimp_display_shell_key_to_state (gint key);
gimp_display_shell_key_to_state (gint key);
static GdkModifierType
gimp_display_shell_button_to_state (gint button);
gimp_display_shell_button_to_state (gint button);
static void gimp_display_shell_proximity_in (GimpDisplayShell *shell);
static void gimp_display_shell_proximity_out (GimpDisplayShell *shell);
static void gimp_display_shell_proximity_in (GimpDisplayShell *shell);
static void gimp_display_shell_proximity_out (GimpDisplayShell *shell);
static void gimp_display_shell_check_device_cursor (GimpDisplayShell *shell);
static void gimp_display_shell_check_device_cursor (GimpDisplayShell *shell);
static void gimp_display_shell_start_scrolling (GimpDisplayShell *shell,
const GdkEvent *event,
GdkModifierType state,
gint x,
gint y);
static void gimp_display_shell_stop_scrolling (GimpDisplayShell *shell,
const GdkEvent *event);
static void gimp_display_shell_handle_scrolling (GimpDisplayShell *shell,
GdkModifierType state,
gint x,
gint y);
static void gimp_display_shell_start_scrolling (GimpDisplayShell *shell,
const GdkEvent *event,
GdkModifierType state,
gint x,
gint y);
static void gimp_display_shell_stop_scrolling (GimpDisplayShell *shell,
const GdkEvent *event);
static void gimp_display_shell_handle_scrolling (GimpDisplayShell *shell,
GdkModifierType state,
gint x,
gint y);
static void gimp_display_shell_space_pressed (GimpDisplayShell *shell,
const GdkEvent *event);
static void gimp_display_shell_space_released (GimpDisplayShell *shell,
const GdkEvent *event,
const GimpCoords *image_coords);
static void gimp_display_shell_space_pressed (GimpDisplayShell *shell,
const GdkEvent *event);
static void gimp_display_shell_space_released (GimpDisplayShell *shell,
const GdkEvent *event,
const GimpCoords *image_coords);
static gboolean gimp_display_shell_tab_pressed (GimpDisplayShell *shell,
const GdkEventKey *event);
static gboolean gimp_display_shell_tab_pressed (GimpDisplayShell *shell,
const GdkEventKey *event);
static void gimp_display_shell_update_focus (GimpDisplayShell *shell,
gboolean focus_in,
const GimpCoords *image_coords,
GdkModifierType state);
static void gimp_display_shell_update_cursor (GimpDisplayShell *shell,
const GimpCoords *display_coords,
const GimpCoords *image_coords,
GdkModifierType state,
gboolean update_software_cursor);
static void gimp_display_shell_update_focus (GimpDisplayShell *shell,
gboolean focus_in,
const GimpCoords *image_coords,
GdkModifierType state);
static void gimp_display_shell_update_cursor (GimpDisplayShell *shell,
const GimpCoords *display_coords,
const GimpCoords *image_coords,
GdkModifierType state,
gboolean update_software_cursor);
static gboolean gimp_display_shell_initialize_tool (GimpDisplayShell *shell,
const GimpCoords *image_coords,
GdkModifierType state);
static gboolean gimp_display_shell_initialize_tool (GimpDisplayShell *shell,
const GimpCoords *image_coords,
GdkModifierType state);
static void gimp_display_shell_get_event_coords (GimpDisplayShell *shell,
const GdkEvent *event,
GimpCoords *display_coords,
GdkModifierType *state,
guint32 *time);
static void gimp_display_shell_untransform_event_coords (GimpDisplayShell *shell,
const GimpCoords *display_coords,
GimpCoords *image_coords,
gboolean *update_software_cursor);
static void gimp_display_shell_get_event_coords (GimpDisplayShell *shell,
const GdkEvent *event,
GimpCoords *display_coords,
GdkModifierType *state,
guint32 *time);
static void gimp_display_shell_untransform_event_coords (GimpDisplayShell *shell,
const GimpCoords *display_coords,
GimpCoords *image_coords,
gboolean *update_software_cursor);
static GdkEvent * gimp_display_shell_compress_motion (GdkEvent *initial_event);
static GdkEvent * gimp_display_shell_compress_motion (GdkEvent *initial_event,
GdkEvent **next_event);
/* public functions */
@ -306,6 +312,184 @@ gboolean
gimp_display_shell_canvas_tool_events (GtkWidget *canvas,
GdkEvent *event,
GimpDisplayShell *shell)
{
GdkEvent *next_event = NULL;
gboolean return_val;
g_return_val_if_fail (gtk_widget_get_realized (canvas), FALSE);
return_val = gimp_display_shell_canvas_tool_events_internal (canvas,
event, shell,
&next_event);
if (next_event)
{
gtk_main_do_event (next_event);
gdk_event_free (next_event);
}
return return_val;
}
void
gimp_display_shell_canvas_grab_notify (GtkWidget *canvas,
gboolean was_grabbed,
GimpDisplayShell *shell)
{
GimpDisplay *display;
GimpImage *image;
Gimp *gimp;
/* are we in destruction? */
if (! shell->display || ! gimp_display_get_shell (shell->display))
return;
display = shell->display;
gimp = gimp_display_get_gimp (display);
image = gimp_display_get_image (display);
if (! image)
return;
GIMP_LOG (TOOL_EVENTS, "grab_notify (display %p): was_grabbed = %s",
display, was_grabbed ? "TRUE" : "FALSE");
if (! was_grabbed)
{
if (! gimp_image_is_empty (image))
{
GimpTool *active_tool = tool_manager_get_active (gimp);
if (active_tool && active_tool->focus_display == display)
{
tool_manager_modifier_state_active (gimp, 0, display);
}
}
}
}
void
gimp_display_shell_buffer_stroke (GimpMotionBuffer *buffer,
const GimpCoords *coords,
guint32 time,
GdkModifierType state,
GimpDisplayShell *shell)
{
GimpDisplay *display = shell->display;
Gimp *gimp = gimp_display_get_gimp (display);
GimpTool *active_tool;
active_tool = tool_manager_get_active (gimp);
if (active_tool &&
gimp_tool_control_is_active (active_tool->control))
{
tool_manager_motion_active (gimp,
coords, time, state,
display);
}
}
void
gimp_display_shell_buffer_hover (GimpMotionBuffer *buffer,
const GimpCoords *coords,
GdkModifierType state,
gboolean proximity,
GimpDisplayShell *shell)
{
GimpDisplay *display = shell->display;
Gimp *gimp = gimp_display_get_gimp (display);
GimpTool *active_tool;
active_tool = tool_manager_get_active (gimp);
if (active_tool &&
! gimp_tool_control_is_active (active_tool->control))
{
tool_manager_oper_update_active (gimp,
coords, state, proximity,
display);
}
}
static gboolean
gimp_display_shell_ruler_button_press (GtkWidget *widget,
GdkEventButton *event,
GimpDisplayShell *shell,
GimpOrientationType orientation)
{
GimpDisplay *display = shell->display;
if (display->gimp->busy)
return TRUE;
if (! gimp_display_get_image (display))
return TRUE;
if (event->type == GDK_BUTTON_PRESS && event->button == 1)
{
GimpTool *active_tool = tool_manager_get_active (display->gimp);
if (active_tool)
{
gimp_display_shell_update_focus (shell, TRUE,
NULL, event->state);
if (gimp_display_shell_pointer_grab (shell, NULL, 0))
{
if (gimp_display_shell_keyboard_grab (shell,
(GdkEvent *) event))
{
if (event->state & gimp_get_toggle_behavior_mask ())
{
gimp_sample_point_tool_start_new (active_tool, display);
}
else
{
gimp_guide_tool_start_new (active_tool, display,
orientation);
}
return TRUE;
}
else
{
gimp_display_shell_pointer_ungrab (shell, NULL);
}
}
}
}
return FALSE;
}
gboolean
gimp_display_shell_hruler_button_press (GtkWidget *widget,
GdkEventButton *event,
GimpDisplayShell *shell)
{
return gimp_display_shell_ruler_button_press (widget, event, shell,
GIMP_ORIENTATION_HORIZONTAL);
}
gboolean
gimp_display_shell_vruler_button_press (GtkWidget *widget,
GdkEventButton *event,
GimpDisplayShell *shell)
{
return gimp_display_shell_ruler_button_press (widget, event, shell,
GIMP_ORIENTATION_VERTICAL);
}
/* private functions */
static gboolean
gimp_display_shell_canvas_tool_events_internal (GtkWidget *canvas,
GdkEvent *event,
GimpDisplayShell *shell,
GdkEvent **next_event)
{
GimpDisplay *display;
GimpImage *image;
@ -318,7 +502,7 @@ gimp_display_shell_canvas_tool_events (GtkWidget *canvas,
gboolean return_val = FALSE;
gboolean update_sw_cursor = FALSE;
g_return_val_if_fail (gtk_widget_get_realized (canvas), FALSE);
*next_event = NULL;
/* are we in destruction? */
if (! shell->display || ! gimp_display_get_shell (shell->display))
@ -851,7 +1035,8 @@ gimp_display_shell_canvas_tool_events (GtkWidget *canvas,
if (shell->scrolling ||
motion_mode == GIMP_MOTION_MODE_COMPRESS)
{
compressed_motion = gimp_display_shell_compress_motion (event);
compressed_motion = gimp_display_shell_compress_motion (event,
next_event);
if (compressed_motion && ! shell->scrolling)
{
@ -1240,159 +1425,6 @@ gimp_display_shell_canvas_tool_events (GtkWidget *canvas,
return return_val;
}
void
gimp_display_shell_canvas_grab_notify (GtkWidget *canvas,
gboolean was_grabbed,
GimpDisplayShell *shell)
{
GimpDisplay *display;
GimpImage *image;
Gimp *gimp;
/* are we in destruction? */
if (! shell->display || ! gimp_display_get_shell (shell->display))
return;
display = shell->display;
gimp = gimp_display_get_gimp (display);
image = gimp_display_get_image (display);
if (! image)
return;
GIMP_LOG (TOOL_EVENTS, "grab_notify (display %p): was_grabbed = %s",
display, was_grabbed ? "TRUE" : "FALSE");
if (! was_grabbed)
{
if (! gimp_image_is_empty (image))
{
GimpTool *active_tool = tool_manager_get_active (gimp);
if (active_tool && active_tool->focus_display == display)
{
tool_manager_modifier_state_active (gimp, 0, display);
}
}
}
}
void
gimp_display_shell_buffer_stroke (GimpMotionBuffer *buffer,
const GimpCoords *coords,
guint32 time,
GdkModifierType state,
GimpDisplayShell *shell)
{
GimpDisplay *display = shell->display;
Gimp *gimp = gimp_display_get_gimp (display);
GimpTool *active_tool;
active_tool = tool_manager_get_active (gimp);
if (active_tool &&
gimp_tool_control_is_active (active_tool->control))
{
tool_manager_motion_active (gimp,
coords, time, state,
display);
}
}
void
gimp_display_shell_buffer_hover (GimpMotionBuffer *buffer,
const GimpCoords *coords,
GdkModifierType state,
gboolean proximity,
GimpDisplayShell *shell)
{
GimpDisplay *display = shell->display;
Gimp *gimp = gimp_display_get_gimp (display);
GimpTool *active_tool;
active_tool = tool_manager_get_active (gimp);
if (active_tool &&
! gimp_tool_control_is_active (active_tool->control))
{
tool_manager_oper_update_active (gimp,
coords, state, proximity,
display);
}
}
static gboolean
gimp_display_shell_ruler_button_press (GtkWidget *widget,
GdkEventButton *event,
GimpDisplayShell *shell,
GimpOrientationType orientation)
{
GimpDisplay *display = shell->display;
if (display->gimp->busy)
return TRUE;
if (! gimp_display_get_image (display))
return TRUE;
if (event->type == GDK_BUTTON_PRESS && event->button == 1)
{
GimpTool *active_tool = tool_manager_get_active (display->gimp);
if (active_tool)
{
gimp_display_shell_update_focus (shell, TRUE,
NULL, event->state);
if (gimp_display_shell_pointer_grab (shell, NULL, 0))
{
if (gimp_display_shell_keyboard_grab (shell,
(GdkEvent *) event))
{
if (event->state & gimp_get_toggle_behavior_mask ())
{
gimp_sample_point_tool_start_new (active_tool, display);
}
else
{
gimp_guide_tool_start_new (active_tool, display,
orientation);
}
return TRUE;
}
else
{
gimp_display_shell_pointer_ungrab (shell, NULL);
}
}
}
}
return FALSE;
}
gboolean
gimp_display_shell_hruler_button_press (GtkWidget *widget,
GdkEventButton *event,
GimpDisplayShell *shell)
{
return gimp_display_shell_ruler_button_press (widget, event, shell,
GIMP_ORIENTATION_HORIZONTAL);
}
gboolean
gimp_display_shell_vruler_button_press (GtkWidget *widget,
GdkEventButton *event,
GimpDisplayShell *shell)
{
return gimp_display_shell_ruler_button_press (widget, event, shell,
GIMP_ORIENTATION_VERTICAL);
}
/* private functions */
static GdkModifierType
gimp_display_shell_key_to_state (gint key)
{
@ -1952,15 +1984,21 @@ gimp_display_shell_untransform_event_coords (GimpDisplayShell *shell,
* remove them from the queue, and return the most recent motion event.
* Otherwise it will return NULL.
*
* If `next_event` is non-NULL upon return, the caller must dispatch and
* free this event after handling the motion event.
*
* The gimp_display_shell_compress_motion function source may be re-used under
* the XFree86-style license. <adam@gimp.org>
*/
static GdkEvent *
gimp_display_shell_compress_motion (GdkEvent *initial_event)
gimp_display_shell_compress_motion (GdkEvent *initial_event,
GdkEvent **next_event)
{
GdkEvent *last_motion = NULL;
GtkWidget *widget;
*next_event = NULL;
if (initial_event->any.type != GDK_MOTION_NOTIFY)
return NULL;
@ -1973,12 +2011,15 @@ gimp_display_shell_compress_motion (GdkEvent *initial_event)
if (!event)
{
/* Do nothing */
break;
}
else if ((gtk_get_event_widget (event) == widget) &&
(event->any.type == GDK_MOTION_NOTIFY) &&
(event->any.window == initial_event->any.window) &&
(event->motion.state == initial_event->motion.state) &&
(event->motion.device == initial_event->motion.device))
{
/* Discard previous motion event */
if (last_motion)
gdk_event_free (last_motion);
@ -1986,27 +2027,8 @@ gimp_display_shell_compress_motion (GdkEvent *initial_event)
}
else
{
GSList *remaining_events = NULL;
/* Put the event back at the front of the queue... the hard way :P */
remaining_events = g_slist_prepend (remaining_events, event);
while (gdk_events_pending ())
if ((event = gdk_event_get ()))
remaining_events = g_slist_prepend (remaining_events, event);
remaining_events = g_slist_reverse (remaining_events);
while (remaining_events)
{
event = remaining_events->data;
gdk_event_put (event);
gdk_event_free (event);
remaining_events = g_slist_delete_link (remaining_events,
remaining_events);
}
/* Let the caller dispatch the event */
*next_event = event;
break;
}