Skip to content

Commit 41a820c

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. clutter-project#98
1 parent a54afe0 commit 41a820c

File tree

1 file changed

+102
-110
lines changed

1 file changed

+102
-110
lines changed

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);
@@ -483,16 +490,6 @@ mx_kinetic_scroll_view_dispose (GObject *object)
483490
G_OBJECT_CLASS (mx_kinetic_scroll_view_parent_class)->dispose (object);
484491
}
485492

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

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

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

838889
g_object_get (G_OBJECT (settings),
839890
"drag-threshold", &threshold, NULL);
840-
g_assert (priv->motion_buffer->len > 0);
841-
motion = &g_array_index (priv->motion_buffer,
842-
MxKineticScrollViewMotion, 0);
891+
g_assert (priv->n_motions > 0);
892+
motion = &priv->motion_origin;
843893

844894
dx = ABS (motion->x - x);
845895
dy = ABS (motion->y - y);
@@ -921,10 +971,10 @@ motion_event_cb (ClutterActor *actor,
921971
return FALSE;
922972
}
923973

924-
g_assert (priv->motion_buffer->len > 0);
974+
g_assert (priv->n_motions > 0);
925975
LOG_DEBUG (scroll, "motion dx=%f dy=%f",
926-
ABS (g_array_index (priv->motion_buffer, MxKineticScrollViewMotion, priv->motion_buffer->len - 1).x - x),
927-
ABS (g_array_index (priv->motion_buffer, MxKineticScrollViewMotion, priv->motion_buffer->len - 1).y - y));
976+
ABS (priv->motion_last.x - x),
977+
ABS (priv->motion_last.y - y));
928978

929979
if (priv->child)
930980
{
@@ -934,10 +984,7 @@ motion_event_cb (ClutterActor *actor,
934984
mx_scrollable_get_adjustments (MX_SCROLLABLE (priv->child),
935985
&hadjust, &vadjust);
936986

937-
g_assert (priv->last_motion < priv->motion_buffer->len);
938-
motion = &g_array_index (priv->motion_buffer,
939-
MxKineticScrollViewMotion,
940-
priv->last_motion);
987+
motion = &priv->motion_last;
941988

942989
if (!priv->align_tested)
943990
{
@@ -979,18 +1026,7 @@ motion_event_cb (ClutterActor *actor,
9791026
}
9801027
}
9811028

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

9961032
return swallow;
@@ -1306,7 +1342,8 @@ release_event (MxKineticScrollView *scroll,
13061342

13071343
priv->device = NULL;
13081344
priv->sequence = NULL;
1309-
priv->last_motion = 0;
1345+
reset_motion_events (scroll);
1346+
13101347
return FALSE;
13111348
}
13121349

@@ -1326,40 +1363,24 @@ release_event (MxKineticScrollView *scroll,
13261363
MxAdjustment *hadjust, *vadjust;
13271364
glong time_diff;
13281365
guint duration;
1329-
gint i;
13301366

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

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

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

13641385
if (motion_time.tv_sec == release_time.tv_sec)
13651386
time_diff = release_time.tv_usec - motion_time.tv_usec;
@@ -1401,13 +1422,13 @@ release_event (MxKineticScrollView *scroll,
14011422
"event_x = %f, event_y = %f, y = %f, nx = %f, ny = %f, "
14021423
"n = %f, frac = %f, x_origin = %f, y_origin = %f, "
14031424
"time_diff = %lu, duration = %u, "
1404-
"priv->last_motion = %u, priv->dx = %f, "
1425+
"priv->n_motions = %u, priv->dx = %f, "
14051426
"priv->dy = %f, priv->decel_rate = %f, "
14061427
"priv->overshoot = %f, priv->accumulated_delta = %f, "
14071428
"priv->acceleration_factor = %f",
14081429
G_STRFUNC, x_pos, y_pos, event_x, event_y,
14091430
y, nx, ny, n, frac, x_origin, y_origin, time_diff,
1410-
duration, priv->last_motion, priv->dx, priv->dy,
1431+
duration, priv->n_motions, priv->dx, priv->dy,
14111432
priv->decel_rate, priv->overshoot,
14121433
priv->accumulated_delta, priv->acceleration_factor);
14131434

@@ -1518,14 +1539,14 @@ release_event (MxKineticScrollView *scroll,
15181539
"d = %f, ax = %f, ay = %f, y = %f, nx = %f, ny = %f, "
15191540
"n = %f, frac = %f, x_origin = %f, y_origin = %f, "
15201541
"time_diff = %lu, duration = %u, "
1521-
"priv->last_motion = %u, priv->dx = %f, "
1542+
"priv->n_motions = %u, priv->dx = %f, "
15221543
"priv->dy = %f, priv->decel_rate = %f, "
15231544
"priv->overshoot = %f, priv->accumulated_delta = %f, "
15241545
"priv->acceleration_factor = %f",
15251546
G_STRFUNC, x_pos, y_pos, event_x, event_y, value,
15261547
lower, upper, step_increment, page_size, d, ax, ay, y,
15271548
nx, ny, n, frac, x_origin, y_origin, time_diff,
1528-
duration, priv->last_motion, priv->dx, priv->dy,
1549+
duration, priv->n_motions, priv->dx, priv->dy,
15291550
priv->decel_rate, priv->overshoot,
15301551
priv->accumulated_delta, priv->acceleration_factor);
15311552

@@ -1566,7 +1587,7 @@ release_event (MxKineticScrollView *scroll,
15661587
priv->device = NULL;
15671588

15681589
/* Reset motion event buffer */
1569-
priv->last_motion = 0;
1590+
reset_motion_events (scroll);
15701591

15711592
if (!decelerating)
15721593
clamp_adjustments (scroll, priv->clamp_duration, TRUE, TRUE);
@@ -1582,27 +1603,27 @@ press_event (MxKineticScrollView *scroll,
15821603
MxKineticScrollViewPrivate *priv = scroll->priv;
15831604
ClutterActor *actor = (ClutterActor *) scroll;
15841605
ClutterActor *stage = clutter_actor_get_stage (actor);
1585-
MxKineticScrollViewMotion *motion;
1606+
gfloat event_x, event_y;
15861607

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

15911612
/* Reset motion buffer */
1592-
priv->last_motion = 0;
1593-
motion = &g_array_index (priv->motion_buffer, MxKineticScrollViewMotion, 0);
1594-
motion->x = x;
1595-
motion->y = y;
1613+
reset_motion_events (scroll);
1614+
1615+
event_x = x;
1616+
event_y = y;
15961617

15971618
LOG_DEBUG (scroll, "initial point(%fx%f)", x, y);
15981619

15991620
if (clutter_actor_transform_stage_point (actor, x, y,
1600-
&motion->x, &motion->y))
1621+
&event_x, &event_y))
16011622
{
16021623
guint threshold;
16031624
MxSettings *settings = mx_settings_get_default ();
16041625

1605-
g_get_current_time (&motion->time);
1626+
add_motion_event (scroll, event_x, event_y);
16061627

16071628
if (priv->deceleration_timeline)
16081629
{
@@ -1815,9 +1836,6 @@ mx_kinetic_scroll_view_init (MxKineticScrollView *self)
18151836
MxKineticScrollViewPrivate *priv = self->priv =
18161837
KINETIC_SCROLL_VIEW_PRIVATE (self);
18171838

1818-
priv->motion_buffer =
1819-
g_array_sized_new (FALSE, TRUE, sizeof (MxKineticScrollViewMotion), 30);
1820-
g_array_set_size (priv->motion_buffer, 3);
18211839
priv->decel_rate = 1.1f;
18221840
priv->button = 1;
18231841
priv->scroll_policy = MX_SCROLL_POLICY_BOTH;
@@ -1925,32 +1943,6 @@ mx_kinetic_scroll_view_get_deceleration (MxKineticScrollView *scroll)
19251943
return scroll->priv->decel_rate;
19261944
}
19271945

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

0 commit comments

Comments
 (0)