Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 3ca39d03 authored by Harry Cutts's avatar Harry Cutts
Browse files

Support touchpad gesture properties in IDC files

Specific touchpads often require small tweaks to the Gestures library's
gesture properties to work well. Examples of such tweaks include palm
thresholds, or the parameters to the stationary wiggle filter. To
support this, allow properties to be set using the 'gestureProp.' prefix
in IDC files, with the spaces in property names replaced by underscores.
For example, to set the "Pressure Calibration Offset" property to 30,
add this line to the relevant IDC file:

    gestureProp.Pressure_Calibration_Offset = 30

Only single-valued boolean, integer, or real properties can be set this
way. There's currently no use case for strings. The only use case for
arrays is for acceleration curves, which wouldn't work well with this
system anyway since they'd be overridden by the pointer speed settings.

Bug: 271251605
Test: atest inputflinger_tests
Test: connect Apple Magic Trackpad 2, check properties are set correctly
      in dumpsys input
Change-Id: I1bf97c9753e48c00d3dc3098cb676f7baebc84ce
parent 186fb6f2
Loading
Loading
Loading
Loading
+7 −0
Original line number Diff line number Diff line
@@ -18,7 +18,10 @@

#include <android-base/result.h>
#include <utils/Tokenizer.h>

#include <string>
#include <unordered_map>
#include <unordered_set>

namespace android {

@@ -57,6 +60,9 @@ public:
     */
    void addProperty(const std::string& key, const std::string& value);

    /* Returns a set of all property keys starting with the given prefix. */
    std::unordered_set<std::string> getKeysWithPrefix(const std::string& prefix) const;

    /* Gets the value of a property and parses it.
     * Returns true and sets outValue if the key was found and its value was parsed successfully.
     * Otherwise returns false and does not modify outValue.  (Also logs a warning.)
@@ -65,6 +71,7 @@ public:
    bool tryGetProperty(const std::string& key, bool& outValue) const;
    bool tryGetProperty(const std::string& key, int32_t& outValue) const;
    bool tryGetProperty(const std::string& key, float& outValue) const;
    bool tryGetProperty(const std::string& key, double& outValue) const;

    /* Adds all values from the specified property map. */
    void addAll(const PropertyMap* map);
+1 −0
Original line number Diff line number Diff line
@@ -167,6 +167,7 @@ cc_library {

cc_defaults {
    name: "libinput_fuzz_defaults",
    cpp_std: "c++20",
    host_supported: true,
    shared_libs: [
        "libutils",
+29 −0
Original line number Diff line number Diff line
@@ -16,6 +16,8 @@

#define LOG_TAG "PropertyMap"

#include <cstdlib>

#include <input/PropertyMap.h>
#include <log/log.h>

@@ -44,6 +46,16 @@ void PropertyMap::addProperty(const std::string& key, const std::string& value)
    mProperties.emplace(key, value);
}

std::unordered_set<std::string> PropertyMap::getKeysWithPrefix(const std::string& prefix) const {
    std::unordered_set<std::string> keys;
    for (const auto& [key, _] : mProperties) {
        if (key.starts_with(prefix)) {
            keys.insert(key);
        }
    }
    return keys;
}

bool PropertyMap::hasProperty(const std::string& key) const {
    return mProperties.find(key) != mProperties.end();
}
@@ -102,6 +114,23 @@ bool PropertyMap::tryGetProperty(const std::string& key, float& outValue) const
    return true;
}

bool PropertyMap::tryGetProperty(const std::string& key, double& outValue) const {
    std::string stringValue;
    if (!tryGetProperty(key, stringValue) || stringValue.length() == 0) {
        return false;
    }

    char* end;
    double value = strtod(stringValue.c_str(), &end);
    if (*end != '\0') {
        ALOGW("Property key '%s' has invalid value '%s'.  Expected a double.", key.c_str(),
              stringValue.c_str());
        return false;
    }
    outValue = value;
    return true;
}

void PropertyMap::addAll(const PropertyMap* map) {
    for (const auto& [key, value] : map->mProperties) {
        mProperties.emplace(key, value);
+6 −0
Original line number Diff line number Diff line
@@ -20,6 +20,7 @@
#include <optional>

#include <android/input.h>
#include <ftl/enum.h>
#include <input/PrintTools.h>
#include <linux/input-event-codes.h>
#include <log/log_main.h>
@@ -216,6 +217,11 @@ void TouchpadInputMapper::dump(std::string& dump) {
std::list<NotifyArgs> TouchpadInputMapper::configure(nsecs_t when,
                                                     const InputReaderConfiguration* config,
                                                     uint32_t changes) {
    if (!changes) {
        // First time configuration
        mPropertyProvider.loadPropertiesFromIdcFile(getDeviceContext().getConfiguration());
    }

    if (!changes || (changes & InputReaderConfiguration::CHANGE_DISPLAY_INFO)) {
        std::optional<int32_t> displayId = mPointerController->getDisplayId();
        ui::Rotation orientation = ui::ROTATION_0;
+76 −0
Original line number Diff line number Diff line
@@ -84,6 +84,29 @@ std::string PropertyProvider::dump() const {
    return dump;
}

void PropertyProvider::loadPropertiesFromIdcFile(const PropertyMap& idcProperties) {
    // For compatibility with the configuration file syntax, gesture property names in IDC files are
    // prefixed with "gestureProp." and have spaces replaced by underscores. So, for example, the
    // configuration key "gestureProp.Palm_Width" refers to the "Palm Width" property.
    const std::string gesturePropPrefix = "gestureProp.";
    for (const std::string key : idcProperties.getKeysWithPrefix(gesturePropPrefix)) {
        std::string propertyName = key.substr(gesturePropPrefix.length());
        for (size_t i = 0; i < propertyName.length(); i++) {
            if (propertyName[i] == '_') {
                propertyName[i] = ' ';
            }
        }

        auto it = mProperties.find(propertyName);
        if (it != mProperties.end()) {
            it->second.trySetFromIdcProperty(idcProperties, key);
        } else {
            ALOGE("Gesture property \"%s\" specified in IDC file does not exist for this device.",
                  propertyName.c_str());
        }
    }
}

GesturesProp* PropertyProvider::createIntArrayProperty(const std::string& name, int* loc,
                                                       size_t count, const int* init) {
    const auto [it, inserted] =
@@ -211,6 +234,59 @@ void GesturesProp::setRealValues(const std::vector<double>& values) {
    setValues(std::get<double*>(mDataPointer), values);
}

namespace {

// Helper to std::visit with lambdas.
template <typename... V>
struct Visitor : V... {};
// explicit deduction guide (not needed as of C++20)
template <typename... V>
Visitor(V...) -> Visitor<V...>;

} // namespace

void GesturesProp::trySetFromIdcProperty(const android::PropertyMap& idcProperties,
                                         const std::string& propertyName) {
    if (mCount != 1) {
        ALOGE("Gesture property \"%s\" is an array, and so cannot be set in an IDC file.",
              mName.c_str());
        return;
    }
    bool parsedSuccessfully = false;
    Visitor setVisitor{
            [&](int*) {
                int32_t value;
                parsedSuccessfully = idcProperties.tryGetProperty(propertyName, value);
                if (parsedSuccessfully) {
                    setIntValues({value});
                }
            },
            [&](GesturesPropBool*) {
                bool value;
                parsedSuccessfully = idcProperties.tryGetProperty(propertyName, value);
                if (parsedSuccessfully) {
                    setBoolValues({value});
                }
            },
            [&](double*) {
                double value;
                parsedSuccessfully = idcProperties.tryGetProperty(propertyName, value);
                if (parsedSuccessfully) {
                    setRealValues({value});
                }
            },
            [&](const char**) {
                ALOGE("Gesture property \"%s\" is a string, and so cannot be set in an IDC file.",
                      mName.c_str());
            },
    };
    std::visit(setVisitor, mDataPointer);

    ALOGE_IF(!parsedSuccessfully, "Gesture property \"%s\" could set due to a type mismatch.",
             mName.c_str());
    return;
}

template <typename T, typename U>
const std::vector<T> GesturesProp::getValues(U* dataPointer) const {
    if (mGetter != nullptr) {
Loading