diff --git a/docs/reference/gdk/gdk4-sections.txt b/docs/reference/gdk/gdk4-sections.txt index 6834b99d93..5e8a503a66 100644 --- a/docs/reference/gdk/gdk4-sections.txt +++ b/docs/reference/gdk/gdk4-sections.txt @@ -494,6 +494,7 @@ gdk_event_get_modifier_state gdk_event_get_position gdk_event_get_axes gdk_event_get_axis +gdk_event_get_history gdk_event_get_pointer_emulated gdk_event_triggers_context_menu gdk_button_event_get_button @@ -525,7 +526,6 @@ gdk_touchpad_event_get_pinch_scale gdk_pad_event_get_axis_value gdk_pad_event_get_button gdk_pad_event_get_group_mode -gdk_motion_event_get_history gdk_events_get_angle diff --git a/gdk/gdkdevice.h b/gdk/gdkdevice.h index bd5a55bcf4..844b7f69fc 100644 --- a/gdk/gdkdevice.h +++ b/gdk/gdkdevice.h @@ -85,22 +85,20 @@ typedef enum { GDK_DEVICE_TYPE_FLOATING } GdkDeviceType; -/* We don't allocate each coordinate this big, but we use it to - * be ANSI compliant and avoid accessing past the defined limits. - */ -#define GDK_MAX_TIMECOORD_AXES 128 /** * GdkTimeCoord: * @time: The timestamp for this event. - * @axes: the values of the device’s axes. + * @flags: Flags indicating what axes are present + * @axes: axis values * * A #GdkTimeCoord stores a single event in a motion history. */ struct _GdkTimeCoord { guint32 time; - gdouble axes[GDK_MAX_TIMECOORD_AXES]; + GdkAxisFlags flags; + double axes[GDK_AXIS_LAST]; }; GDK_AVAILABLE_IN_ALL diff --git a/gdk/gdkevents.c b/gdk/gdkevents.c index e1a58006cb..b3e379ebe2 100644 --- a/gdk/gdkevents.c +++ b/gdk/gdkevents.c @@ -619,6 +619,114 @@ _gdk_event_unqueue (GdkDisplay *display) return event; } +/* + * If the last N events in the event queue are smooth scroll events + * for the same surface and device, combine them into one. + */ +void +gdk_event_queue_handle_scroll_compression (GdkDisplay *display) +{ + GList *l; + GdkSurface *surface = NULL; + GdkDevice *device = NULL; + GdkEvent *last_event = NULL; + GList *scrolls = NULL; + double delta_x, delta_y; + GArray *history = NULL; + GdkTimeCoord hist; + + l = g_queue_peek_tail_link (&display->queued_events); + + while (l) + { + GdkEvent *event = l->data; + + if (event->flags & GDK_EVENT_PENDING) + break; + + if (event->event_type != GDK_SCROLL || + gdk_scroll_event_get_direction (event) != GDK_SCROLL_SMOOTH) + break; + + if (surface != NULL && + surface != event->surface) + break; + + if (device != NULL && + device != event->device) + break; + + if (!last_event) + last_event = event; + + surface = event->surface; + device = event->device; + scrolls = l; + + l = l->prev; + } + + delta_x = delta_y = 0; + + while (scrolls && scrolls->next != NULL) + { + GdkEvent *event = scrolls->data; + GList *next = scrolls->next; + double dx, dy; + + if (!history) + history = g_array_new (FALSE, TRUE, sizeof (GdkTimeCoord)); + + gdk_scroll_event_get_deltas (event, &dx, &dy); + delta_x += dx; + delta_y += dy; + + memset (&hist, 0, sizeof (GdkTimeCoord)); + hist.time = gdk_event_get_time (event); + hist.flags = GDK_AXIS_FLAG_DELTA_X | GDK_AXIS_FLAG_DELTA_Y; + hist.axes[GDK_AXIS_DELTA_X] = dx; + hist.axes[GDK_AXIS_DELTA_Y] = dy; + + g_array_append_val (history, hist); + + gdk_event_unref (event); + g_queue_delete_link (&display->queued_events, scrolls); + scrolls = next; + } + + if (scrolls) + { + GdkEvent *old_event, *event; + double dx, dy; + + old_event = scrolls->data; + + gdk_scroll_event_get_deltas (old_event, &dx, &dy); + event = gdk_scroll_event_new (surface, + device, + gdk_event_get_source_device (old_event), + gdk_event_get_device_tool (old_event), + gdk_event_get_time (old_event), + gdk_event_get_modifier_state (old_event), + delta_x + dx, + delta_y + dy, + gdk_scroll_event_is_stop (old_event)); + + ((GdkScrollEvent *)event)->history = history; + + g_queue_delete_link (&display->queued_events, scrolls); + g_queue_push_tail (&display->queued_events, event); + } + + if (g_queue_get_length (&display->queued_events) == 1 && + g_queue_peek_head_link (&display->queued_events) == scrolls) + { + GdkFrameClock *clock = gdk_surface_get_frame_clock (surface); + if (clock) /* might be NULL if surface was destroyed */ + gdk_frame_clock_request_phase (clock, GDK_FRAME_CLOCK_PHASE_FLUSH_EVENTS); + } +} + static void gdk_motion_event_push_history (GdkEvent *event, GdkEvent *history_event) @@ -634,14 +742,20 @@ gdk_motion_event_push_history (GdkEvent *event, device = gdk_event_get_device (history_event); n_axes = gdk_device_get_n_axes (device); - for (i = 0; i <= MIN (n_axes, GDK_MAX_TIMECOORD_AXES); i++) - gdk_event_get_axis (history_event, i, &hist.axes[i]); + memset (&hist, 0, sizeof (GdkTimeCoord)); + hist.time = gdk_event_get_time (history_event); + hist.flags = gdk_device_get_axes (device); + + for (i = 0; i < n_axes; i++) + { + GdkAxisUse use = gdk_device_get_axis_use (device, i); + gdk_event_get_axis (history_event, use, &hist.axes[use]); + } if (G_UNLIKELY (!self->history)) self->history = g_array_new (FALSE, TRUE, sizeof (GdkTimeCoord)); g_array_append_val (self->history, hist); - } void @@ -710,7 +824,7 @@ _gdk_event_queue_handle_motion_compression (GdkDisplay *display) { GdkFrameClock *clock = gdk_surface_get_frame_clock (pending_motion_surface); if (clock) /* might be NULL if surface was destroyed */ - gdk_frame_clock_request_phase (clock, GDK_FRAME_CLOCK_PHASE_FLUSH_EVENTS); + gdk_frame_clock_request_phase (clock, GDK_FRAME_CLOCK_PHASE_FLUSH_EVENTS); } } @@ -2158,6 +2272,8 @@ gdk_scroll_event_finalize (GdkEvent *event) GdkScrollEvent *self = (GdkScrollEvent *) event; g_clear_object (&self->tool); + if (self->history) + g_array_free (self->history, TRUE); GDK_EVENT_SUPER (self)->finalize (event); } @@ -2777,35 +2893,51 @@ gdk_motion_event_new (GdkSurface *surface, } /** - * gdk_motion_event_get_history: - * @event: (type GdkMotionEvent): a motion #GdkEvent + * gdk_event_get_history: + * @event: a motion or scroll #GdkEvent * @out_n_coords: (out): Return location for the length of the returned array * - * Retrieves the history of the @event motion, as a list of time and - * coordinates. + * Retrieves the history of the @event, as a list of time and coordinates. + * + * The history includes events that are not delivered to the application + * because they occurred in the same frame as @event. + * + * Note that only motion and scroll events record history, and motion + * events only if one of the mouse buttons is down. * * Returns: (transfer container) (array length=out_n_coords) (nullable): an * array of time and coordinates */ GdkTimeCoord * -gdk_motion_event_get_history (GdkEvent *event, - guint *out_n_coords) +gdk_event_get_history (GdkEvent *event, + guint *out_n_coords) { - GdkMotionEvent *self = (GdkMotionEvent *) event; + GArray *history; g_return_val_if_fail (GDK_IS_EVENT (event), NULL); - g_return_val_if_fail (GDK_IS_EVENT_TYPE (event, GDK_MOTION_NOTIFY), NULL); + g_return_val_if_fail (GDK_IS_EVENT_TYPE (event, GDK_MOTION_NOTIFY) || + GDK_IS_EVENT_TYPE (event, GDK_SCROLL), NULL); g_return_val_if_fail (out_n_coords != NULL, NULL); - if (self->history && - self->history->len > 0) + if (GDK_IS_EVENT_TYPE (event, GDK_MOTION_NOTIFY)) + { + GdkMotionEvent *self = (GdkMotionEvent *) event; + history = self->history; + } + else + { + GdkScrollEvent *self = (GdkScrollEvent *) event; + history = self->history; + } + + if (history && history->len > 0) { GdkTimeCoord *result; - *out_n_coords = self->history->len; + *out_n_coords = history->len; - result = g_malloc (sizeof (GdkTimeCoord) * self->history->len); - memcpy (result, self->history->data, sizeof (GdkTimeCoord) * self->history->len); + result = g_malloc (sizeof (GdkTimeCoord) * history->len); + memcpy (result, history->data, sizeof (GdkTimeCoord) * history->len); return result; } diff --git a/gdk/gdkevents.h b/gdk/gdkevents.h index e702717541..9abb9a96cd 100644 --- a/gdk/gdkevents.h +++ b/gdk/gdkevents.h @@ -378,6 +378,9 @@ gboolean gdk_event_get_axis (GdkEvent *event, GdkAxisUse axis_use, double *value); GDK_AVAILABLE_IN_ALL +GdkTimeCoord * gdk_event_get_history (GdkEvent *event, + guint *out_n_coords); +GDK_AVAILABLE_IN_ALL gboolean gdk_event_get_pointer_emulated (GdkEvent *event); GDK_AVAILABLE_IN_ALL @@ -392,6 +395,7 @@ GDK_AVAILABLE_IN_ALL void gdk_scroll_event_get_deltas (GdkEvent *event, double *delta_x, double *delta_y); + GDK_AVAILABLE_IN_ALL gboolean gdk_scroll_event_is_stop (GdkEvent *event); GDK_AVAILABLE_IN_ALL @@ -470,9 +474,6 @@ gboolean gdk_grab_broken_event_get_implicit (GdkEvent *event) GDK_AVAILABLE_IN_ALL GType gdk_motion_event_get_type (void) G_GNUC_CONST; -GDK_AVAILABLE_IN_ALL -GdkTimeCoord * gdk_motion_event_get_history (GdkEvent *event, - guint *out_n_coords); GDK_AVAILABLE_IN_ALL GType gdk_delete_event_get_type (void) G_GNUC_CONST; diff --git a/gdk/gdkeventsprivate.h b/gdk/gdkeventsprivate.h index 0fd0b3e908..a21e458746 100644 --- a/gdk/gdkeventsprivate.h +++ b/gdk/gdkeventsprivate.h @@ -211,6 +211,9 @@ struct _GdkTouchEvent * @pointer_emulated: whether the scroll event was the result of * a pointer emulation * @tool: a #GdkDeviceTool + * @history: (element-type GdkScrollHistory): array of times and deltas + * for other scroll events that were compressed before delivering the + * current event * * Generated from button presses for the buttons 4 to 7. Wheel mice are * usually configured to generate button press events for buttons 4 and 5 @@ -232,6 +235,7 @@ struct _GdkScrollEvent gboolean pointer_emulated; gboolean is_stop; GdkDeviceTool *tool; + GArray *history; /* */ }; /* diff --git a/gdk/gdkinternals.h b/gdk/gdkinternals.h index e30872edc6..b26aac73eb 100644 --- a/gdk/gdkinternals.h +++ b/gdk/gdkinternals.h @@ -123,6 +123,7 @@ GList* _gdk_event_queue_append (GdkDisplay *display, GdkEvent *event); void _gdk_event_queue_handle_motion_compression (GdkDisplay *display); +void gdk_event_queue_handle_scroll_compression (GdkDisplay *display); void _gdk_event_queue_flush (GdkDisplay *display); gboolean _gdk_cairo_surface_extents (cairo_surface_t *surface, diff --git a/gdk/gdksurface.c b/gdk/gdksurface.c index 1c5aeb180a..9e5946794d 100644 --- a/gdk/gdksurface.c +++ b/gdk/gdksurface.c @@ -2366,6 +2366,7 @@ _gdk_windowing_got_event (GdkDisplay *display, * candidate it queues up flushing the event queue. */ _gdk_event_queue_handle_motion_compression (display); + gdk_event_queue_handle_scroll_compression (display); } /** diff --git a/gdk/gdktypes.h b/gdk/gdktypes.h index e461f2bfcf..9ad7879367 100644 --- a/gdk/gdktypes.h +++ b/gdk/gdktypes.h @@ -247,6 +247,8 @@ typedef enum { * @GDK_AXIS_IGNORE: the axis is ignored. * @GDK_AXIS_X: the axis is used as the x axis. * @GDK_AXIS_Y: the axis is used as the y axis. + * @GDK_AXIS_DELTA_X: the axis is used as the scroll x delta + * @GDK_AXIS_DELTA_Y: the axis is used as the scroll y delta * @GDK_AXIS_PRESSURE: the axis is used for pressure information. * @GDK_AXIS_XTILT: the axis is used for x tilt information. * @GDK_AXIS_YTILT: the axis is used for y tilt information. @@ -269,6 +271,8 @@ typedef enum GDK_AXIS_IGNORE, GDK_AXIS_X, GDK_AXIS_Y, + GDK_AXIS_DELTA_X, + GDK_AXIS_DELTA_Y, GDK_AXIS_PRESSURE, GDK_AXIS_XTILT, GDK_AXIS_YTILT, @@ -283,6 +287,8 @@ typedef enum * GdkAxisFlags: * @GDK_AXIS_FLAG_X: X axis is present * @GDK_AXIS_FLAG_Y: Y axis is present + * @GDK_AXIS_FLAG_DELTA_X: Scroll X delta axis is present + * @GDK_AXIS_FLAG_DELTA_Y: Scroll Y delta axis is present * @GDK_AXIS_FLAG_PRESSURE: Pressure axis is present * @GDK_AXIS_FLAG_XTILT: X tilt axis is present * @GDK_AXIS_FLAG_YTILT: Y tilt axis is present @@ -297,6 +303,8 @@ typedef enum { GDK_AXIS_FLAG_X = 1 << GDK_AXIS_X, GDK_AXIS_FLAG_Y = 1 << GDK_AXIS_Y, + GDK_AXIS_FLAG_DELTA_X = 1 << GDK_AXIS_DELTA_X, + GDK_AXIS_FLAG_DELTA_Y = 1 << GDK_AXIS_DELTA_Y, GDK_AXIS_FLAG_PRESSURE = 1 << GDK_AXIS_PRESSURE, GDK_AXIS_FLAG_XTILT = 1 << GDK_AXIS_XTILT, GDK_AXIS_FLAG_YTILT = 1 << GDK_AXIS_YTILT, diff --git a/gtk/gtkgesturestylus.c b/gtk/gtkgesturestylus.c index da0dbc868f..094a759553 100644 --- a/gtk/gtkgesturestylus.c +++ b/gtk/gtkgesturestylus.c @@ -278,8 +278,8 @@ gtk_gesture_stylus_get_axes (GtkGestureStylus *gesture, **/ gboolean gtk_gesture_stylus_get_backlog (GtkGestureStylus *gesture, - GdkTimeCoord **backlog, - guint *n_elems) + GdkTimeCoord **backlog, + guint *n_elems) { GdkEvent *event; GArray *backlog_array; @@ -292,7 +292,7 @@ gtk_gesture_stylus_get_backlog (GtkGestureStylus *gesture, event = gesture_get_current_event (gesture); if (event && GDK_IS_EVENT_TYPE (event, GDK_MOTION_NOTIFY)) - history = gdk_motion_event_get_history (event, &n_coords); + history = gdk_event_get_history (event, &n_coords); if (!history) return FALSE;