+ t->is_pointer = false;
+ t->pinned.is_pinned = true;
+ t->pinned.center_x = t->x;
+ t->pinned.center_y = t->y;
+ }
+}
+
+static int
+tp_touch_active(struct tp_dispatch *tp, struct tp_touch *t)
+{
+ return (t->state == TOUCH_BEGIN || t->state == TOUCH_UPDATE) &&
+ !t->palm.is_palm &&
+ !t->pinned.is_pinned && tp_button_touch_active(tp, t);
+}
+
+void
+tp_set_pointer(struct tp_dispatch *tp, struct tp_touch *t)
+{
+ struct tp_touch *tmp = NULL;
+
+ /* Only set the touch as pointer if we don't have one yet */
+ tp_for_each_touch(tp, tmp) {
+ if (tmp->is_pointer)
+ return;
+ }
+
+ if (tp_touch_active(tp, t))
+ t->is_pointer = true;
+}
+
+static void
+tp_palm_detect(struct tp_dispatch *tp, struct tp_touch *t, uint64_t time)
+{
+ const int PALM_TIMEOUT = 200; /* ms */
+ const int DIRECTIONS = NE|E|SE|SW|W|NW;
+
+ /* If labelled a touch as palm, we unlabel as palm when
+ we move out of the palm edge zone within the timeout, provided
+ the direction is within 45 degrees of the horizontal.
+ */
+ if (t->palm.is_palm) {
+ if (time < t->palm.time + PALM_TIMEOUT &&
+ (t->x > tp->palm.left_edge && t->x < tp->palm.right_edge)) {
+ int dirs = vector_get_direction(t->x - t->palm.x, t->y - t->palm.y);
+ if ((dirs & DIRECTIONS) && !(dirs & ~DIRECTIONS)) {
+ t->palm.is_palm = false;
+ tp_set_pointer(tp, t);
+ }
+ }
+ return;
+ }
+
+ /* palm must start in exclusion zone, it's ok to move into
+ the zone without being a palm */
+ if (t->state != TOUCH_BEGIN ||
+ (t->x > tp->palm.left_edge && t->x < tp->palm.right_edge))
+ return;
+
+ /* don't detect palm in software button areas, it's
+ likely that legitimate touches start in the area
+ covered by the exclusion zone */
+ if (tp->buttons.is_clickpad &&
+ tp_button_is_inside_softbutton_area(tp, t))
+ return;
+
+ t->palm.is_palm = true;
+ t->palm.time = time;
+ t->palm.x = t->x;
+ t->palm.y = t->y;
+}
+
+static void
+tp_process_state(struct tp_dispatch *tp, uint64_t time)
+{
+ struct tp_touch *t;
+ struct tp_touch *first = tp_get_touch(tp, 0);
+ unsigned int i;
+
+ for (i = 0; i < tp->ntouches; i++) {
+ t = tp_get_touch(tp, i);
+
+ /* semi-mt finger postions may "jump" when nfingers changes */
+ if (tp->semi_mt && tp->nfingers_down != tp->old_nfingers_down)
+ tp_motion_history_reset(t);
+
+ if (i >= tp->real_touches && t->state != TOUCH_NONE) {
+ t->x = first->x;
+ t->y = first->y;
+ if (!t->dirty)
+ t->dirty = first->dirty;
+ }
+