Salsa: Update the try_touch_experiment UI

This changes the way the participants actually apply the treatments.
Previously, they had to copy paste each treatment and it became clear
that this was a huge pain.  Similarly, the UI once you had copied it
into Crosh was confusing.

This change replaces the individual treatment commands with one giant
command you copy for the entire experiment.  This command includes
information on *all* of the treatments and if you pass it into the new
try_touch_experiment program it will allow you to switch back and
forth between the treatments however you please with a simple curses
interface.  Beyond UI, it checks repeatedly now to make sure that
everything was applied correctly, hopefully getting rid of any errors in
how the treatments are viewed.

Hopefully this makes it easier for users to compare the experiments and
flip back and forth between them, etc.

try_touch_experiment will replace the command that is currently just
a function in Crosh.  Crosh will be patched so that the command now just
runs this instead.

BUG=chromium-os:38637
TEST=For the server-side changes, a dev server is running at
cmooney.mtv:8081 where you can see the differences.  Manual testing of
the new try_touch_experiment was done on a Link to confirm it works there.

Original-Change-Id: Ia0090629e8d988e8f517ff83ceac03a9bf808c19
Originally-Signed-off-by: Charlie Mooney <charliemooney@chromium.org>
Originally-Reviewed-on: https://gerrit.chromium.org/gerrit/42772
(cherry picked from commit 83aa6c2daa67332c573a18e3eaf5c091ebd7222b)

Change-Id: I26350be8f4c3e0a48a50d49e6f5b190dfd12b10a
Signed-off-by: Charlie Mooney <charliemooney@chromium.org>
Reviewed-on: https://gerrit.chromium.org/gerrit/44051
Reviewed-by: Andrew de los Reyes <adlr@chromium.org>
diff --git a/Makefile b/Makefile
index e66b97f..35797a6 100644
--- a/Makefile
+++ b/Makefile
@@ -114,6 +114,7 @@
 	-I..
 
 LID_TOUCHPAD_HELPER=lid_touchpad_helper
+TRY_TOUCHPAD_EXPERIMENT=salsa/try_touch_experiment
 
 # Local compilation needs these flags, esp for code coverage testing
 ifeq (g++,$(CXX))
@@ -149,6 +150,7 @@
 
 all: $(SONAME)
 	$(MAKE) -C $(LID_TOUCHPAD_HELPER)
+	$(MAKE) -C $(TRY_TOUCHPAD_EXPERIMENT)
 
 $(SONAME): $(SO_OBJECTS)
 	$(CXX) -shared -o $@ $(SO_OBJECTS) -Wl,-h$(SONAME:$(OBJDIR)/%=%) \
@@ -166,6 +168,7 @@
 
 install: $(SONAME)
 	$(MAKE) -C $(LID_TOUCHPAD_HELPER) install
+	$(MAKE) -C $(TRY_TOUCHPAD_EXPERIMENT) install
 	install -D -m 0755 $(SONAME) \
 		$(DESTDIR)$(LIBDIR)/$(SONAME:$(OBJDIR)/%=%)
 	ln -s $(SONAME:$(OBJDIR)/%=%) \
@@ -175,6 +178,7 @@
 
 clean:
 	$(MAKE) -C $(LID_TOUCHPAD_HELPER) clean
+	$(MAKE) -C $(TRY_TOUCHPAD_EXPERIMENT) clean
 	rm -rf $(OBJDIR) $(DEPDIR) $(TEST_EXE) html app.info app.info.orig
 
 # Unittest coverage
diff --git a/salsa/css/style.css b/salsa/css/style.css
index 00c4c36..2d8acbb 100644
--- a/salsa/css/style.css
+++ b/salsa/css/style.css
@@ -126,26 +126,6 @@
     border-color: #ccc
 }
 
-.inactive {
-    cursor: pointer;
-}
-
-.inactive .details {
-    display: none;
-}
-
-.active .details {
-    display: block;
-}
-
-.inactive .tooltip {
-    display: block;
-}
-
-.active .tooltip {
-    display: none;
-}
-
 .delete {
     float: right;
 }
diff --git a/salsa/filters.py b/salsa/filters.py
index 4648262..f65a82c 100644
--- a/salsa/filters.py
+++ b/salsa/filters.py
@@ -35,3 +35,28 @@
     p_value = sum([binomial_pmf(i, n, 0.5) for i in range(w, n + 1)])
 
     return p_value
+
+def encode_property(prop):
+    """ Return an encoded version of a property by merging it's encoded
+    name and value with the hex value for the : character (3a)
+
+    NAME:VALUE
+    """
+    return "%s3a%s" % (prop.name.encode("hex"), prop.value.encode("hex"))
+
+def encode_treatment(treatment, properties):
+    """ Return an encoded version of a treatment by merging it's encoded
+    properties with the hex value for the , character (0x2c)
+
+    PROPERTY 1,PROPERTY 2, PROPERTY 3
+    """
+    return "2c".join([encode_property(p) for p in properties
+                      if p.parent_key() == treatment.key()])
+
+def encode_experiment(experiment, treatments, properties):
+    """ Return an encoded version of an experiment by merging it's encoded
+    treatments with the hex value for the + character (0x2b)
+
+    TREATMENT 1+TREATMENT 2+TREATMENT 3
+    """
+    return "2b".join([encode_treatment(t, properties) for t in treatments])
diff --git a/salsa/salsa.py b/salsa/salsa.py
index f8256a8..5cb1286 100644
--- a/salsa/salsa.py
+++ b/salsa/salsa.py
@@ -24,6 +24,7 @@
 jinja_environment.filters['row_class'] = filters.row_class
 jinja_environment.filters['sign_test'] = filters.sign_test
 jinja_environment.filters['variance'] = filters.variance
+jinja_environment.filters['encode_experiment'] = filters.encode_experiment
 
 def load_experiment(experiment_id):
     experiment = Experiment.gql('WHERE __key__ = KEY(:1)', experiment_id).get()
diff --git a/salsa/templates/participate.html b/salsa/templates/participate.html
index 9c334c6..466705f 100644
--- a/salsa/templates/participate.html
+++ b/salsa/templates/participate.html
@@ -6,15 +6,6 @@
   {{ super() }}
 
   <script language="javascript" type="text/javascript">
-    function expand(treatment) {
-      // Expand the size of a treatment and shrink all the others
-      treatments = treatment.parentElement.children;
-      for (var i = 0; i < treatments.length; i++) {
-        treatments[i].className = 'treatment inactive';
-      }
-      treatment.className = 'treatment active';
-    }
-
     function submitReview() {
       var keys = [
         {% for treatment in treatments %}
@@ -143,14 +134,14 @@
   {% endif %}
 
   <p>
-    Below is a list of treatments for this experiment.  Each treatment
-    represents a variation on the touchpad processing algorithm.  To participate
-    in this experiment, please try out each of the treatments, and then rank
-    your experiences.
+    This experiment consists of {{ treatments|length }} </i>treatments</i>.
+    Each treatment is a variation on the touchpad settings.  To participate in
+    this experiment, you will try out each of these treatments and then
+    provide us with feedback regarding their performance.
   </p>
 
   <p>
-    Each treatment has a command you can run on your Chromebook to temporarily
+    Below you will find a command you can run on your Chromebook to temporarily
     alter the way your touchpad works.  To try out an experiment, simply open
     up Crosh with Ctrl + Alt + t and paste in the given command.  Note that to
     paste into the terminal you must either right click and press paste or use
@@ -158,10 +149,10 @@
   </p>
 
   <p>
-    Once you have applied a treatment, please follow the instructions on how
-    to best experience the changes and judge them more accurately.
-    Finally, once you have tried all of the treatments, use the form on the
-    right to rank your experiences and click "Submit" to record your responses.
+    You will be given on-screen instructions on how to switch between the
+    different treatments once you run that command. Finally, once you have 
+    tried all of the treatments, use the form on the right to rank your 
+    experiences and click "Submit" to record your responses.
   </p>
 
   <ul>
@@ -171,35 +162,16 @@
         {{ experiment.owner }}
       </a>
     <li> Created: {{ experiment.created }}
-    <li> Treatments:
-      {% for treatment in treatments %}
-        <div class="treatment {% if loop.first %}
-                                active
-                              {% else %}
-                                inactive
-                              {% endif %}" onclick="expand(this)">
-          <h2> Treatment #{{ loop.index0 }} </h2>
-          <div class="tooltip"> click to expand... </div>
-          <div class="details">
-            <div class="command">
-              try_touch_experiment
-              {# Convert the properties and their values to a hex encoding #}
-              {# Properties are separated from values by colons (hex 3a) #}
-              {# Value/Property pairs are separated by commas (hex 2c) #}
-              {% for prop in properties if
-                    prop.parent_key() == treatment.key() %}
-                {{- prop.name.encode("hex") -}}
-                3a
-                {{- prop.value.encode("hex") -}}
-                {%- if not loop.last %}2c{% endif -%}
-              {% endfor %}
-            </div>
-
-            {{ experiment.instructions|safe }}
-          </div>
-        </div>
-      {% endfor %}
+    <li> Treatments: {{ treatments|length }}
   </ul>
+
+  <div class="treatment">
+    <h2> Experiment Command </h2>
+    <div class="command">
+      try_touch_experiment
+      {{ experiment|encode_experiment(treatments, properties) }}
+    </div>
+  </div>
 {% endblock %}
 
 {% block second_column %}
diff --git a/salsa/try_touch_experiment/Makefile b/salsa/try_touch_experiment/Makefile
new file mode 100644
index 0000000..76fc713
--- /dev/null
+++ b/salsa/try_touch_experiment/Makefile
@@ -0,0 +1,68 @@
+# Copyright (c) 2013 The Chromium OS Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+OBJDIR = obj
+
+OBJECTS=\
+	$(OBJDIR)/try_touch_experiment.o \
+	$(OBJDIR)/salsa_experiment_runner.o \
+	$(OBJDIR)/treatment.o \
+	$(OBJDIR)/property.o \
+	$(OBJDIR)/experiment.o
+
+DESTDIR = .
+
+CXXFLAGS+=\
+	-g \
+	-fno-exceptions \
+	-fno-strict-aliasing \
+	-fPIC \
+	-Wall \
+	-Wclobbered \
+	-Wempty-body \
+	-Werror \
+	-Wignored-qualifiers \
+	-Wmissing-field-initializers \
+	-Wsign-compare \
+	-Wtype-limits \
+	-Wuninitialized \
+	-D__STDC_FORMAT_MACROS=1 \
+	-D_FILE_OFFSET_BITS=64 \
+	-DGESTURES_INTERNAL=1 \
+	-I..
+
+PKG_CONFIG ?= pkg-config
+BASE_VER ?= 180609
+PC_DEPS = libchrome-$(BASE_VER)
+PC_CFLAGS := $(shell $(PKG_CONFIG) --cflags $(PC_DEPS))
+PC_LIBS := $(shell $(PKG_CONFIG) --libs $(PC_DEPS))
+
+CXXFLAGS += $(PC_CFLAGS)
+
+LINK_FLAGS=\
+	$(PC_LIBS) \
+	-lcurses
+
+EXE=try_touch_experiment
+
+all: $(EXE)
+
+$(EXE): $(OBJECTS)
+	$(CXX) -o $@ $(CXXFLAGS) $(OBJECTS) $(LINK_FLAGS)
+
+$(OBJDIR)/%.o : %.cc
+	mkdir -p $(OBJDIR) || true
+	$(CXX) $(CXXFLAGS) -c -o $@ $<
+
+$(OBJDIR)/%.o : %.c
+	mkdir -p $(OBJDIR) || true
+	$(CC) $(CFLAGS) -c -o $@ $<
+
+install: $(EXE)
+	install -D -m 0755 $(EXE) $(DESTDIR)/usr/sbin/$(EXE)
+
+clean:
+	rm -rf $(OBJDIR) $(EXE)
+
+.PHONY : clean all
diff --git a/salsa/try_touch_experiment/experiment.cc b/salsa/try_touch_experiment/experiment.cc
new file mode 100644
index 0000000..649bf3e
--- /dev/null
+++ b/salsa/try_touch_experiment/experiment.cc
@@ -0,0 +1,43 @@
+// Copyright (c) 2013 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "experiment.h"
+
+using base::SplitString;
+using std::string;
+using std::vector;
+
+Experiment::Experiment() : is_valid_(false) {}
+
+bool Experiment::valid() const {
+  return is_valid_;
+}
+
+Experiment::Experiment(string const &experiment_string) {
+  vector<string> treatment_strings;
+  SplitString(experiment_string, '+', &treatment_strings);
+
+  is_valid_ = true;
+  for (vector<string>::const_iterator it = treatment_strings.begin();
+       it != treatment_strings.end(); ++it) {
+    treatments_.push_back(Treatment(*it));
+    is_valid_ = is_valid_ && treatments_.back().valid();
+  }
+}
+
+bool Experiment::Reset() const {
+  if (!is_valid_)
+    return false;
+  return treatments_[0].Reset();
+}
+
+bool Experiment::ApplyTreatment(unsigned int treatment_num) const {
+  if (treatment_num >= treatments_.size())
+    return false;
+  return treatments_[treatment_num].Apply();
+}
+
+int Experiment::Size() const {
+  return treatments_.size();
+}
diff --git a/salsa/try_touch_experiment/experiment.h b/salsa/try_touch_experiment/experiment.h
new file mode 100644
index 0000000..d0e0364
--- /dev/null
+++ b/salsa/try_touch_experiment/experiment.h
@@ -0,0 +1,29 @@
+// Copyright (c) 2013 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef GESTURES_SALSA_TRY_TOUCH_EXPERIMENT_EXPERIMENT_H_
+#define GESTURES_SALSA_TRY_TOUCH_EXPERIMENT_EXPERIMENT_H_
+
+#include <string>
+#include <vector>
+#include <base/string_split.h>
+#include "treatment.h"
+
+class Experiment {
+  public:
+    Experiment();
+    explicit Experiment(const std::string &experiment_string);
+
+    bool ApplyTreatment(unsigned int treatment_num) const;
+    bool Reset() const;
+
+    int Size() const;
+    bool valid() const;
+
+  private:
+    std::vector<Treatment> treatments_;
+    bool is_valid_;
+};
+
+#endif  // GESTURES_SALSA_TRY_TOUCH_EXPERIMENT_EXPERIMENT_H_
diff --git a/salsa/try_touch_experiment/property.cc b/salsa/try_touch_experiment/property.cc
new file mode 100644
index 0000000..e46309e
--- /dev/null
+++ b/salsa/try_touch_experiment/property.cc
@@ -0,0 +1,96 @@
+// Copyright (c) 2013 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "property.h"
+
+using base::SplitString;
+using base::StringPrintf;
+using std::string;
+using std::vector;
+
+Property::Property() : is_valid_(false) {}
+
+bool Property::valid() const {
+  return is_valid_;
+}
+
+Property::Property(string const &property_string) {
+  vector<string> parts;
+  SplitString(property_string, ':', &parts);
+
+  name_ = "";
+  if (parts.size() == 2) {
+    name_ = parts[0];
+    value_ = atof(parts[1].c_str());
+    device_ = GetDeviceNumber();
+    property_number_ = GetPropertyNumber();
+    old_value_ = GetCurrentValue();
+    is_valid_ = true;
+  }
+
+  if (name_.length() == 0)
+    is_valid_ = false;
+}
+
+bool Property::Reset() const {
+  return SetValue(old_value_);
+}
+
+bool Property::Apply() const {
+  return SetValue(value_);
+}
+
+bool Property::SetValue(double new_value) const {
+  string command = StringPrintf("DISPLAY=:0 xinput set-prop %d %d %f",
+                                device_, property_number_, new_value);
+
+  double current_value = GetCurrentValue();
+  for (int i = 0; i < MAX_RETRIES && current_value != new_value; i++) {
+    RunCommand(command);
+    current_value = GetCurrentValue();
+  }
+
+  return (abs(current_value - new_value) <= MAX_ALLOWABLE_DIFFERENCE);
+}
+
+int Property::GetDeviceNumber() const {
+  return atoi(RunCommand("/opt/google/touchpad/tpcontrol listdev").c_str());
+}
+
+int Property::GetPropertyNumber() const {
+  string command = StringPrintf("DISPLAY=:0 xinput list-props %d"
+                                " | grep '%s'"
+                                " | sed -e 's/[^(]*(\\([0-9]*\\)):.*/\\1/'",
+                                device_,
+                                name_.c_str());
+  return atoi(RunCommand(command).c_str());
+}
+
+double Property::GetCurrentValue() const {
+  string command = StringPrintf("DISPLAY=:0 xinput list-props %d"
+                                " | grep '%s'"
+                                " | sed -e 's/[^:]*:\\s*\\([0-9.]*\\)$/\\1/'",
+                                device_,
+                                name_.c_str());
+  return atoi(RunCommand(command).c_str());
+}
+
+string Property::RunCommand(string const &command) const {
+  // Run a command from a shell and return the contents of stdout
+  FILE* pipe = popen(command.c_str(), "r");
+  if (!pipe)
+    return "";
+
+  char buffer[256];
+  string result = "";
+  while (!feof(pipe)) {
+    if (fgets(buffer, 256, pipe) != NULL)
+      result += buffer;
+  }
+
+  pclose(pipe);
+
+  TrimWhitespaceASCII(result, TRIM_ALL, &result);
+  return result;
+}
diff --git a/salsa/try_touch_experiment/property.h b/salsa/try_touch_experiment/property.h
new file mode 100644
index 0000000..604c170
--- /dev/null
+++ b/salsa/try_touch_experiment/property.h
@@ -0,0 +1,48 @@
+// Copyright (c) 2013 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef GESTURES_SALSA_TRY_TOUCH_EXPERIMENT_PROPERTY_H_
+#define GESTURES_SALSA_TRY_TOUCH_EXPERIMENT_PROPERTY_H_
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string>
+#include <vector>
+#include <base/stringprintf.h>
+#include <base/string_split.h>
+#include <base/string_util.h>
+
+#define MAX_RETRIES 5
+#define MAX_ALLOWABLE_DIFFERENCE 0.0001
+
+class Property {
+  public:
+    Property();
+    explicit Property(const std::string &property_string);
+
+    bool Apply() const;
+    bool Reset() const;
+
+    bool valid() const;
+
+  private:
+    double GetCurrentValue() const;
+    int GetPropertyNumber() const;
+    int GetDeviceNumber() const;
+    std::string RunCommand(std::string const &command) const;
+    bool SetValue(double new_value) const;
+
+    std::string name_;
+    double value_;
+    double old_value_;
+    int device_;
+    int property_number_;
+
+    bool is_valid_;
+};
+
+
+
+
+#endif  // GESTURES_SALSA_TRY_TOUCH_EXPERIMENT_PROPERTY_H_
diff --git a/salsa/try_touch_experiment/salsa_experiment_runner.cc b/salsa/try_touch_experiment/salsa_experiment_runner.cc
new file mode 100644
index 0000000..8b1f531
--- /dev/null
+++ b/salsa/try_touch_experiment/salsa_experiment_runner.cc
@@ -0,0 +1,100 @@
+// Copyright (c) 2013 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "salsa_experiment_runner.h"
+
+using std::string;
+
+bool SalsaExperimentRunner::LoadExperiment(string const &exp_string) {
+  string decoded_string = Decode(exp_string);
+  if (decoded_string.empty())
+    return false;
+
+  exp_ = Experiment(decoded_string);
+  return exp_.valid();
+}
+
+string SalsaExperimentRunner::Decode(string const &exp_string) const {
+  // Hex encoded strings always have an even length
+  if (exp_string.length() % 2 != 0)
+    return "";
+
+  // Decode the string from hex, any non-hex characters invalidate it
+  string decoded_string = "";
+  for (string::const_iterator it = exp_string.begin();
+       it != exp_string.end(); ++it) {
+    int val1 = HexDigitToInt(*it);
+    int val2 = HexDigitToInt(*++it);
+    if (val1 < 0 || val2 < 0)
+      return "";
+    else
+      decoded_string.push_back(static_cast<char>(val1 * 16 + val2));
+  }
+
+  return decoded_string;
+}
+
+void SalsaExperimentRunner::EndCurses() {
+  endwin();
+}
+
+void SalsaExperimentRunner::StartCurses() {
+  initscr();
+  cbreak();
+  noecho();
+  refresh();
+  atexit(SalsaExperimentRunner::EndCurses);
+}
+
+void SalsaExperimentRunner::run() const {
+  int current_treatment = -1;
+  bool success = false;
+  char key_press = '0';
+
+  SalsaExperimentRunner::StartCurses();
+  WINDOW* win = newwin(19, 59, 0, 0);
+  while (key_press != 'q') {
+    int selected_treatment = key_press - '0';
+    if (selected_treatment >= 0 && selected_treatment < exp_.Size()) {
+      current_treatment = selected_treatment;
+      success = exp_.ApplyTreatment(current_treatment);
+    }
+
+    box(win, 0, 0);
+    mvwprintw(win, 1, 15, "  _____       _           ");
+    mvwprintw(win, 2, 15, " / ____|     | |          ");
+    mvwprintw(win, 3, 15, "| (___   __ _| |___  __ _ ");
+    mvwprintw(win, 4, 15, " \\___ \\ / _` | / __|/ _` |");
+    mvwprintw(win, 5, 15, " ____) | (_| | \\__ \\ (_| |");
+    mvwprintw(win, 6, 15, "|_____/ \\__,_|_|___/\\__,_|");
+
+    mvwprintw(win, 9, 2, "Thanks for your participation!");
+
+    if (success)
+      mvwprintw(win, 11, 2, "You are currently experiencing treatment #%d",
+                current_treatment);
+    else
+      mvwprintw(win, 11, 2, "There was an error applying treatment #%d. "
+                            "Try again.", current_treatment);
+    mvwprintw(win, 12, 2, "Available treatments: 0 -> %d", (exp_.Size() - 1));
+
+    mvwprintw(win, 14, 2, "Commands:");
+    mvwprintw(win, 15, 6, "Number keys -- Change treatment");
+    mvwprintw(win, 16, 6, ("q          -- Quit and restore your old settings"));
+    wrefresh(win);
+
+    key_press = getch();
+  }
+
+  if (!exp_.Reset()) {
+    wclear(win);
+    wprintw(win, "WARNING! Some of your setting may not have been reset to ");
+    wprintw(win, "their original values.  If you experience bad touchpad ");
+    wprintw(win, "behavior, you can restore them manually by logging out ");
+    wprintw(win, "and logging back in.  Sorry for the inconvenience.");
+    wrefresh(win);
+  }
+
+  delwin(win);
+}
diff --git a/salsa/try_touch_experiment/salsa_experiment_runner.h b/salsa/try_touch_experiment/salsa_experiment_runner.h
new file mode 100644
index 0000000..ebf3fa8
--- /dev/null
+++ b/salsa/try_touch_experiment/salsa_experiment_runner.h
@@ -0,0 +1,26 @@
+// Copyright (c) 2013 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef GESTURES_SALSA_TRY_TOUCH_EXPERIMENT_RUNNER_H_
+#define GESTURES_SALSA_TRY_TOUCH_EXPERIMENT_RUNNER_H_
+
+#include <ncurses.h>
+#include <string>
+#include <base/string_util.h>
+#include "experiment.h"
+
+class SalsaExperimentRunner {
+  public:
+    bool LoadExperiment(const std::string &exp_string);
+    void run() const;
+
+  private:
+    static void StartCurses();
+    static void EndCurses();
+    std::string Decode(const std::string &exp_string) const;
+
+    Experiment exp_;
+};
+
+#endif  // GESTURES_SALSA_TRY_TOUCH_EXPERIMENT_RUNNER_H_
diff --git a/salsa/try_touch_experiment/treatment.cc b/salsa/try_touch_experiment/treatment.cc
new file mode 100644
index 0000000..3ae8bb4
--- /dev/null
+++ b/salsa/try_touch_experiment/treatment.cc
@@ -0,0 +1,45 @@
+// Copyright (c) 2013 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "treatment.h"
+
+using base::SplitString;
+using std::string;
+using std::vector;
+
+Treatment::Treatment() : is_valid_(false) {}
+
+bool Treatment::valid() const {
+  return is_valid_;
+}
+
+Treatment::Treatment(string const &treatment_string) {
+  vector<string> property_strings;
+  SplitString(treatment_string, ',', &property_strings);
+
+  is_valid_ = true;
+  for (vector<string>::const_iterator it = property_strings.begin();
+       it != property_strings.end(); ++it) {
+    properties_.push_back(Property(*it));
+    is_valid_ = is_valid_ && properties_.back().valid();
+  }
+}
+
+bool Treatment::Reset() const {
+  for (vector<Property>::const_iterator it = properties_.begin();
+       it != properties_.end(); ++it) {
+    if (!it->Reset())
+      return false;
+  }
+  return true;
+}
+
+bool Treatment::Apply() const {
+  for (vector<Property>::const_iterator it = properties_.begin();
+       it != properties_.end(); ++it) {
+    if (!it->Apply())
+      return false;
+  }
+  return true;
+}
diff --git a/salsa/try_touch_experiment/treatment.h b/salsa/try_touch_experiment/treatment.h
new file mode 100644
index 0000000..2e7ccf3
--- /dev/null
+++ b/salsa/try_touch_experiment/treatment.h
@@ -0,0 +1,31 @@
+// Copyright (c) 2013 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef GESTURES_SALSA_TRY_TOUCH_EXPERIMENT_TREATMENT_H_
+#define GESTURES_SALSA_TRY_TOUCH_EXPERIMENT_TREATMENT_H_
+
+#include <string>
+#include <vector>
+#include <base/string_split.h>
+#include "property.h"
+
+class Treatment {
+  public:
+    Treatment();
+    explicit Treatment(const std::string &treatment_string);
+
+    bool Apply() const;
+    bool Reset() const;
+
+    bool valid() const;
+
+  private:
+    std::vector<Property> properties_;
+    bool is_valid_;
+};
+
+
+
+
+#endif  // GESTURES_SALSA_TRY_TOUCH_EXPERIMENT_TREATMENT_H_
diff --git a/salsa/try_touch_experiment/try_touch_experiment.cc b/salsa/try_touch_experiment/try_touch_experiment.cc
new file mode 100644
index 0000000..40cffd9
--- /dev/null
+++ b/salsa/try_touch_experiment/try_touch_experiment.cc
@@ -0,0 +1,27 @@
+// Copyright (c) 2013 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <iostream>
+#include "salsa_experiment_runner.h"
+
+using std::cout;
+using std::endl;
+
+int main(int argc, char** argv) {
+  if (argc != 2) {
+    cout << "Error: You must supply exactly ONE parameter" << endl;
+    cout << "Usage: " << argv[0] << " SALSA_EXPERIMENT_CODE" << endl;
+    return 1;
+  }
+
+  SalsaExperimentRunner runner;
+  if (!runner.LoadExperiment(argv[1])) {
+    cout << "Error: Unable to load that experiment.  Please" << endl;
+    cout << "ensure you copy/pasted it correctly and try again." << endl;
+    return 1;
+  }
+
+  runner.run();
+  return 0;
+}
diff --git a/src/immediate_interpreter.cc b/src/immediate_interpreter.cc
index 6ea3999..4c894bb 100644
--- a/src/immediate_interpreter.cc
+++ b/src/immediate_interpreter.cc
@@ -666,6 +666,7 @@
                e = prev_gs_fingers_.end();
            it != e; ++it) {
         if (!hwstate.GetFingerState(*it)) {
+          Log("Found a finger lifting during a scroll!!!!!!!!!");
           current_gesture_type_ =
               current_gesture_type_ == kGestureTypeScroll ?
               kGestureTypeFling : kGestureTypeSwipeLift;