Skip to content

Commit a5610ad

Browse files
author
Philip Withnall
committed
kinetic-scroll-view: Replace motion buffer with a moving average
The motion buffer stored each motion event which happened in a sequence (for example, a single swipe) on the kinetic scroll view, in order that the average event coordinates and time could be computed at the end of the sequence (on releasing the touchscreen). This could lead to problems with very long event sequences — potentially allocating a huge event array and leading to allocation failure. Avoid that by computing the average of the events as they are received. #98
1 parent 66bf2f4 commit a5610ad

1 file changed

Lines changed: 102 additions & 110 deletions

File tree

mx/mx-kinetic-scroll-view.c

Lines changed: 102 additions & 110 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@
4444
#include "mx-scrollable.h"
4545
#include "mx-focusable.h"
4646
#include <math.h>
47+
#include <string.h>
4748

4849
#define _KINETIC_DEBUG 0
4950

@@ -98,9 +99,21 @@ struct _MxKineticScrollViewPrivate
9899

99100
MxAutomaticScroll in_automatic_scroll;
100101

101-
/* Mouse motion event information */
102-
GArray *motion_buffer;
103-
guint last_motion;
102+
/* Mouse motion event information.
103+
* @motion_origin is the details of the first motion event in a sequence.
104+
* @motion_last is the most recent motion event in a sequence.
105+
* @motion_total is the total value of all the motion events in a sequence.
106+
* FIXME: The code does not currently handle overflow here.
107+
* @n_motions is the number of events used in the calculation of
108+
* @motion_total.
109+
*
110+
* @motion_origin, @motion_last and @motion_total are valid iff
111+
* @n_motions > 0.
112+
*/
113+
MxKineticScrollViewMotion motion_origin;
114+
MxKineticScrollViewMotion motion_last;
115+
MxKineticScrollViewMotion motion_total;
116+
guint n_motions;
104117

105118
/* Variables for storing acceleration information */
106119
ClutterTimeline *deceleration_timeline;
@@ -310,12 +323,6 @@ mx_kinetic_scroll_view_get_property (GObject *object,
310323
g_value_set_double (value, priv->decel_rate);
311324
break;
312325

313-
/*
314-
case PROP_BUFFER_SIZE :
315-
g_value_set_uint (value, priv->motion_buffer->len);
316-
break;
317-
*/
318-
319326
case PROP_HADJUST:
320327
mx_kinetic_scroll_view_get_adjustments (MX_SCROLLABLE (object),
321328
&adjustment, NULL);
@@ -482,16 +489,6 @@ mx_kinetic_scroll_view_dispose (GObject *object)
482489
G_OBJECT_CLASS (mx_kinetic_scroll_view_parent_class)->dispose (object);
483490
}
484491

485-
static void
486-
mx_kinetic_scroll_view_finalize (GObject *object)
487-
{
488-
MxKineticScrollViewPrivate *priv = MX_KINETIC_SCROLL_VIEW (object)->priv;
489-
490-
g_array_free (priv->motion_buffer, TRUE);
491-
492-
G_OBJECT_CLASS (mx_kinetic_scroll_view_parent_class)->finalize (object);
493-
}
494-
495492
static void
496493
mx_kinetic_scroll_view_get_preferred_width (ClutterActor *actor,
497494
gfloat for_height,
@@ -621,7 +618,6 @@ mx_kinetic_scroll_view_class_init (MxKineticScrollViewClass *klass)
621618
object_class->get_property = mx_kinetic_scroll_view_get_property;
622619
object_class->set_property = mx_kinetic_scroll_view_set_property;
623620
object_class->dispose = mx_kinetic_scroll_view_dispose;
624-
object_class->finalize = mx_kinetic_scroll_view_finalize;
625621

626622
actor_class->get_preferred_width = mx_kinetic_scroll_view_get_preferred_width;
627623
actor_class->get_preferred_height = mx_kinetic_scroll_view_get_preferred_height;
@@ -754,6 +750,61 @@ set_state (MxKineticScrollView *scroll, MxKineticScrollViewState state)
754750
LOG_DEBUG (scroll, "%s: finished setting state to %u", G_STRFUNC, state);
755751
}
756752

753+
/* Add a motion event to the rolling average of motion events. */
754+
static void
755+
add_motion_event (MxKineticScrollView *scroll,
756+
gfloat x,
757+
gfloat y)
758+
{
759+
MxKineticScrollViewPrivate *priv = scroll->priv;
760+
GTimeVal tv;
761+
762+
LOG_DEBUG (scroll, "%s: x = %f, y = %f, n_motions = %u",
763+
G_STRFUNC, x, y, priv->n_motions);
764+
765+
g_get_current_time (&tv);
766+
767+
if (priv->n_motions == 0)
768+
{
769+
priv->motion_origin.x = x;
770+
priv->motion_origin.y = y;
771+
priv->motion_origin.time = tv;
772+
773+
memcpy (&priv->motion_last, &priv->motion_origin,
774+
sizeof (priv->motion_last));
775+
memcpy (&priv->motion_total, &priv->motion_origin,
776+
sizeof (priv->motion_total));
777+
778+
priv->n_motions = 1;
779+
}
780+
else if (priv->n_motions < G_MAXUINT)
781+
{
782+
priv->motion_last.x = x;
783+
priv->motion_last.y = y;
784+
priv->motion_last.time = tv;
785+
786+
priv->motion_total.x += x;
787+
priv->motion_total.y += y;
788+
priv->motion_total.time.tv_sec += tv.tv_sec;
789+
priv->motion_total.time.tv_usec += tv.tv_usec;
790+
791+
/* Avoid overflow by only taking this branch if n_motions will not
792+
* overflow. Subsequent motions are ignored. */
793+
priv->n_motions++;
794+
}
795+
}
796+
797+
static void
798+
reset_motion_events (MxKineticScrollView *scroll)
799+
{
800+
MxKineticScrollViewPrivate *priv = scroll->priv;
801+
802+
memset (&priv->motion_origin, 0, sizeof (priv->motion_origin));
803+
memset (&priv->motion_last, 0, sizeof (priv->motion_last));
804+
memset (&priv->motion_total, 0, sizeof (priv->motion_total));
805+
priv->n_motions = 0;
806+
}
807+
757808
static gboolean
758809
motion_event_cb (ClutterActor *actor,
759810
ClutterEvent *event,
@@ -836,9 +887,8 @@ motion_event_cb (ClutterActor *actor,
836887

837888
g_object_get (G_OBJECT (settings),
838889
"drag-threshold", &threshold, NULL);
839-
g_assert (priv->motion_buffer->len > 0);
840-
motion = &g_array_index (priv->motion_buffer,
841-
MxKineticScrollViewMotion, 0);
890+
g_assert (priv->n_motions > 0);
891+
motion = &priv->motion_origin;
842892

843893
dx = ABS (motion->x - x);
844894
dy = ABS (motion->y - y);
@@ -920,10 +970,10 @@ motion_event_cb (ClutterActor *actor,
920970
return FALSE;
921971
}
922972

923-
g_assert (priv->motion_buffer->len > 0);
973+
g_assert (priv->n_motions > 0);
924974
LOG_DEBUG (scroll, "motion dx=%f dy=%f",
925-
ABS (g_array_index (priv->motion_buffer, MxKineticScrollViewMotion, priv->motion_buffer->len - 1).x - x),
926-
ABS (g_array_index (priv->motion_buffer, MxKineticScrollViewMotion, priv->motion_buffer->len - 1).y - y));
975+
ABS (priv->motion_last.x - x),
976+
ABS (priv->motion_last.y - y));
927977

928978
if (priv->child)
929979
{
@@ -933,10 +983,7 @@ motion_event_cb (ClutterActor *actor,
933983
mx_scrollable_get_adjustments (MX_SCROLLABLE (priv->child),
934984
&hadjust, &vadjust);
935985

936-
g_assert (priv->last_motion < priv->motion_buffer->len);
937-
motion = &g_array_index (priv->motion_buffer,
938-
MxKineticScrollViewMotion,
939-
priv->last_motion);
986+
motion = &priv->motion_last;
940987

941988
if (!priv->align_tested)
942989
{
@@ -978,18 +1025,7 @@ motion_event_cb (ClutterActor *actor,
9781025
}
9791026
}
9801027

981-
priv->last_motion ++;
982-
if (priv->last_motion == priv->motion_buffer->len)
983-
{
984-
LOG_DEBUG (scroll, "increase buffer size to %u", priv->last_motion);
985-
g_array_set_size (priv->motion_buffer, priv->last_motion + 1);
986-
}
987-
988-
motion = &g_array_index (priv->motion_buffer,
989-
MxKineticScrollViewMotion, priv->last_motion);
990-
motion->x = x;
991-
motion->y = y;
992-
g_get_current_time (&motion->time);
1028+
add_motion_event (scroll, x, y);
9931029
}
9941030

9951031
return swallow;
@@ -1305,7 +1341,8 @@ release_event (MxKineticScrollView *scroll,
13051341

13061342
priv->device = NULL;
13071343
priv->sequence = NULL;
1308-
priv->last_motion = 0;
1344+
reset_motion_events (scroll);
1345+
13091346
return FALSE;
13101347
}
13111348

@@ -1325,40 +1362,24 @@ release_event (MxKineticScrollView *scroll,
13251362
MxAdjustment *hadjust, *vadjust;
13261363
glong time_diff;
13271364
guint duration;
1328-
gint i;
13291365

13301366
/* Get time delta */
13311367
g_get_current_time (&release_time);
13321368

13331369
/* Get average position/time of last x mouse events */
1334-
priv->last_motion ++;
1335-
if (priv->last_motion == priv->motion_buffer->len)
1336-
{
1337-
LOG_DEBUG (scroll, "increase buffer size to %u",
1338-
priv->last_motion);
1339-
g_array_set_size (priv->motion_buffer, priv->last_motion + 1);
1340-
}
1341-
13421370
x_origin = y_origin = 0;
13431371
motion_time = (GTimeVal){ 0, 0 };
1344-
for (i = 0; i < priv->last_motion; i++)
1345-
{
1346-
MxKineticScrollViewMotion *motion =
1347-
&g_array_index (priv->motion_buffer, MxKineticScrollViewMotion, i);
13481372

1349-
/* FIXME: This doesn't guard against overflows - Should
1350-
* either fix that, or calculate the correct maximum
1351-
* value for the buffer size
1352-
*/
1353-
x_origin += motion->x;
1354-
y_origin += motion->y;
1355-
motion_time.tv_sec += motion->time.tv_sec;
1356-
motion_time.tv_usec += motion->time.tv_usec;
1357-
}
1358-
x_origin = x_origin / priv->last_motion;
1359-
y_origin = y_origin / priv->last_motion;
1360-
motion_time.tv_sec /= priv->last_motion;
1361-
motion_time.tv_usec /= priv->last_motion;
1373+
g_assert (priv->n_motions > 0);
1374+
1375+
x_origin = priv->motion_total.x / priv->n_motions;
1376+
y_origin = priv->motion_total.y / priv->n_motions;
1377+
motion_time.tv_sec = priv->motion_total.time.tv_sec / priv->n_motions;
1378+
motion_time.tv_usec = priv->motion_total.time.tv_usec / priv->n_motions;
1379+
1380+
/* Normalise the GTimeVal. */
1381+
motion_time.tv_sec += motion_time.tv_usec / G_USEC_PER_SEC;
1382+
motion_time.tv_usec %= G_USEC_PER_SEC;
13621383

13631384
if (motion_time.tv_sec == release_time.tv_sec)
13641385
time_diff = release_time.tv_usec - motion_time.tv_usec;
@@ -1400,13 +1421,13 @@ release_event (MxKineticScrollView *scroll,
14001421
"event_x = %f, event_y = %f, y = %f, nx = %f, ny = %f, "
14011422
"n = %f, frac = %f, x_origin = %f, y_origin = %f, "
14021423
"time_diff = %lu, duration = %u, "
1403-
"priv->last_motion = %u, priv->dx = %f, "
1424+
"priv->n_motions = %u, priv->dx = %f, "
14041425
"priv->dy = %f, priv->decel_rate = %f, "
14051426
"priv->overshoot = %f, priv->accumulated_delta = %f, "
14061427
"priv->acceleration_factor = %f",
14071428
G_STRFUNC, x_pos, y_pos, event_x, event_y,
14081429
y, nx, ny, n, frac, x_origin, y_origin, time_diff,
1409-
duration, priv->last_motion, priv->dx, priv->dy,
1430+
duration, priv->n_motions, priv->dx, priv->dy,
14101431
priv->decel_rate, priv->overshoot,
14111432
priv->accumulated_delta, priv->acceleration_factor);
14121433

@@ -1517,14 +1538,14 @@ release_event (MxKineticScrollView *scroll,
15171538
"d = %f, ax = %f, ay = %f, y = %f, nx = %f, ny = %f, "
15181539
"n = %f, frac = %f, x_origin = %f, y_origin = %f, "
15191540
"time_diff = %lu, duration = %u, "
1520-
"priv->last_motion = %u, priv->dx = %f, "
1541+
"priv->n_motions = %u, priv->dx = %f, "
15211542
"priv->dy = %f, priv->decel_rate = %f, "
15221543
"priv->overshoot = %f, priv->accumulated_delta = %f, "
15231544
"priv->acceleration_factor = %f",
15241545
G_STRFUNC, x_pos, y_pos, event_x, event_y, value,
15251546
lower, upper, step_increment, page_size, d, ax, ay, y,
15261547
nx, ny, n, frac, x_origin, y_origin, time_diff,
1527-
duration, priv->last_motion, priv->dx, priv->dy,
1548+
duration, priv->n_motions, priv->dx, priv->dy,
15281549
priv->decel_rate, priv->overshoot,
15291550
priv->accumulated_delta, priv->acceleration_factor);
15301551

@@ -1564,7 +1585,7 @@ release_event (MxKineticScrollView *scroll,
15641585
priv->device = NULL;
15651586

15661587
/* Reset motion event buffer */
1567-
priv->last_motion = 0;
1588+
reset_motion_events (scroll);
15681589

15691590
if (!decelerating)
15701591
clamp_adjustments (scroll, priv->clamp_duration, TRUE, TRUE);
@@ -1580,27 +1601,27 @@ press_event (MxKineticScrollView *scroll,
15801601
MxKineticScrollViewPrivate *priv = scroll->priv;
15811602
ClutterActor *actor = (ClutterActor *) scroll;
15821603
ClutterActor *stage = clutter_actor_get_stage (actor);
1583-
MxKineticScrollViewMotion *motion;
1604+
gfloat event_x, event_y;
15841605

15851606
/* Reset automatic-scroll setting */
15861607
priv->in_automatic_scroll = MX_AUTOMATIC_SCROLL_NONE;
15871608
priv->align_tested = 0;
15881609

15891610
/* Reset motion buffer */
1590-
priv->last_motion = 0;
1591-
motion = &g_array_index (priv->motion_buffer, MxKineticScrollViewMotion, 0);
1592-
motion->x = x;
1593-
motion->y = y;
1611+
reset_motion_events (scroll);
1612+
1613+
event_x = x;
1614+
event_y = y;
15941615

15951616
LOG_DEBUG (scroll, "initial point(%fx%f)", x, y);
15961617

15971618
if (clutter_actor_transform_stage_point (actor, x, y,
1598-
&motion->x, &motion->y))
1619+
&event_x, &event_y))
15991620
{
16001621
guint threshold;
16011622
MxSettings *settings = mx_settings_get_default ();
16021623

1603-
g_get_current_time (&motion->time);
1624+
add_motion_event (scroll, event_x, event_y);
16041625

16051626
if (priv->deceleration_timeline)
16061627
{
@@ -1812,9 +1833,6 @@ mx_kinetic_scroll_view_init (MxKineticScrollView *self)
18121833
MxKineticScrollViewPrivate *priv = self->priv =
18131834
KINETIC_SCROLL_VIEW_PRIVATE (self);
18141835

1815-
priv->motion_buffer =
1816-
g_array_sized_new (FALSE, TRUE, sizeof (MxKineticScrollViewMotion), 30);
1817-
g_array_set_size (priv->motion_buffer, 3);
18181836
priv->decel_rate = 1.1f;
18191837
priv->button = 1;
18201838
priv->scroll_policy = MX_SCROLL_POLICY_BOTH;
@@ -1921,32 +1939,6 @@ mx_kinetic_scroll_view_get_deceleration (MxKineticScrollView *scroll)
19211939
return scroll->priv->decel_rate;
19221940
}
19231941

1924-
/*
1925-
void
1926-
mx_kinetic_scroll_view_set_buffer_size (MxKineticScrollView *scroll,
1927-
guint size)
1928-
{
1929-
MxKineticScrollViewPrivate *priv;
1930-
1931-
g_return_if_fail (MX_IS_KINETIC_SCROLL_VIEW (scroll));
1932-
g_return_if_fail (size > 0);
1933-
1934-
priv = scroll->priv;
1935-
if (priv->motion_buffer->len != size)
1936-
{
1937-
g_array_set_size (priv->motion_buffer, size);
1938-
g_object_notify (G_OBJECT (scroll), "buffer-size");
1939-
}
1940-
}
1941-
1942-
guint
1943-
mx_kinetic_scroll_view_get_buffer_size (MxKineticScrollView *scroll)
1944-
{
1945-
g_return_val_if_fail (MX_IS_KINETIC_SCROLL_VIEW (scroll), 0);
1946-
return scroll->priv->motion_buffer->len;
1947-
}
1948-
*/
1949-
19501942
/**
19511943
* mx_kinetic_scroll_view_set_mouse_button:
19521944
* @scroll: A #MxKineticScrollView

0 commit comments

Comments
 (0)